[PATCH] scripts/recordmcount: support more than 64K ELF sections

From: Dan Aloni
Date: Fri Feb 15 2019 - 14:38:04 EST


Before this change, recordmcount thought that there were zero sections
in an ELF that has more than 64K sections. It did nothing and exited
with exit status 0.

This change is needed so that CONFIG_DYNAMIC_FTRACE continues working
with special compilation flags that put each function in its own
section, a situation sometimes happens with modern compilers.

The other ELF mangling host program helpers in the kernel tree have also
received this treatment in the past. For example, see commit
6845756b29e4 ("modpost: Update 64k section support for binutils
2.18.50").

Some changes here were taken from scripts/sortextable.c.

Signed-off-by: Dan Aloni <dan@xxxxxxxxxxxx>
---
scripts/recordmcount.c | 5 ++
scripts/recordmcount.h | 106 ++++++++++++++++++++++++++++++++++++-----
2 files changed, 99 insertions(+), 12 deletions(-)

diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index a50a2aa963ad..85b5a5105cce 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -409,6 +409,11 @@ is_mcounted_section_name(char const *const txtname)
strcmp(".text.unlikely", txtname) == 0;
}

+static inline int is_shndx_special(unsigned int i)
+{
+ return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
+}
+
/* 32 bit and 64 bit are very similar */
#include "recordmcount.h"
#define RECORD_MCOUNT_64
diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h
index 2e7793735e14..89758521d7e3 100644
--- a/scripts/recordmcount.h
+++ b/scripts/recordmcount.h
@@ -32,6 +32,10 @@
#undef get_mcountsym
#undef get_sym_str_and_relp
#undef do_func
+#undef read_shstrndx
+#undef read_shnum
+#undef write_shnum
+#undef get_symb_shndx
#undef Elf_Addr
#undef Elf_Ehdr
#undef Elf_Shdr
@@ -66,6 +70,10 @@
# define fn_is_fake_mcount fn_is_fake_mcount64
# define MIPS_is_fake_mcount MIPS64_is_fake_mcount
# define mcount_adjust mcount_adjust_64
+# define read_shstrndx read_shstrndx_64
+# define read_shnum read_shnum_64
+# define write_shnum write_shnum_64
+# define get_symb_shndx get_secindex_64
# define Elf_Addr Elf64_Addr
# define Elf_Ehdr Elf64_Ehdr
# define Elf_Shdr Elf64_Shdr
@@ -99,6 +107,10 @@
# define fn_is_fake_mcount fn_is_fake_mcount32
# define MIPS_is_fake_mcount MIPS32_is_fake_mcount
# define mcount_adjust mcount_adjust_32
+# define read_shstrndx read_shstrndx_32
+# define read_shnum read_shnum_32
+# define write_shnum write_shnum_32
+# define get_symb_shndx get_secindex_32
# define Elf_Addr Elf32_Addr
# define Elf_Ehdr Elf32_Ehdr
# define Elf_Shdr Elf32_Shdr
@@ -138,6 +150,58 @@ static void fn_ELF_R_INFO(Elf_Rel *const rp, unsigned sym, unsigned type)
}
static void (*Elf_r_info)(Elf_Rel *const rp, unsigned sym, unsigned type) = fn_ELF_R_INFO;

+static unsigned int read_shstrndx(const Elf_Ehdr * const ehdr,
+ const Elf_Shdr * const shdr0)
+{
+ unsigned int shstrndx = w2(ehdr->e_shstrndx);
+
+ if (shstrndx == SHN_XINDEX)
+ return w(shdr0->sh_link);
+
+ return shstrndx;
+}
+
+static unsigned int read_shnum(const Elf_Ehdr * const ehdr,
+ const Elf_Shdr * const shdr0)
+{
+ unsigned int shnum = w2(ehdr->e_shnum);
+
+ if (shnum == 0)
+ return w(shdr0->sh_size);
+
+ return shnum;
+}
+
+static void write_shnum(Elf_Ehdr * const ehdr,
+ Elf_Shdr * const shdr0,
+ const unsigned int shnum)
+{
+ if (shnum >= SHN_LORESERVE) {
+ ehdr->e_shnum = 0;
+ shdr0->sh_size = shnum;
+ } else {
+ ehdr->e_shnum = shnum;
+ }
+}
+
+/* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
+static unsigned int get_symb_shndx(const Elf_Sym *sym,
+ const Elf_Sym *sym0,
+ const unsigned int *indexes,
+ unsigned int *index_out)
+{
+ if (is_shndx_special(sym->st_shndx))
+ return 0;
+
+ if (sym->st_shndx != SHN_XINDEX) {
+ *index_out = sym->st_shndx;
+ return 1;
+ }
+
+ *index_out = _w(indexes[sym - sym0]);
+ return 1;
+}
+
static int mcount_adjust = 0;

/*
@@ -189,8 +253,9 @@ static void append_func(Elf_Ehdr *const ehdr,
char const *mc_name = (sizeof(Elf_Rela) == rel_entsize)
? ".rela__mcount_loc"
: ".rel__mcount_loc";
- unsigned const old_shnum = w2(ehdr->e_shnum);
uint_t const old_shoff = _w(ehdr->e_shoff);
+ Elf_Shdr *const old_shdr0 = (Elf_Shdr *)(old_shoff + (void *)ehdr);
+ unsigned const old_shnum = read_shnum(ehdr, old_shdr0);
uint_t const old_shstr_sh_size = _w(shstr->sh_size);
uint_t const old_shstr_sh_offset = _w(shstr->sh_offset);
uint_t t = 1 + strlen(mc_name) + _w(shstr->sh_size);
@@ -207,11 +272,18 @@ static void append_func(Elf_Ehdr *const ehdr,
uwrite(fd_map, old_shstr_sh_offset + (void *)ehdr, old_shstr_sh_size);
uwrite(fd_map, mc_name, 1 + strlen(mc_name));

+ /* update shnum (this may write both to ehdr and old_shdr0 */
+ write_shnum(ehdr, old_shdr0, old_shnum + 2);
+
+ /* write updated elf header */
+ ehdr->e_shoff = _w(new_e_shoff);
+ ulseek(fd_map, 0, SEEK_SET);
+ uwrite(fd_map, ehdr, sizeof(*ehdr));
+
/* old(modified) Elf_Shdr table, word-byte aligned */
ulseek(fd_map, t, SEEK_SET);
t += sizeof(Elf_Shdr) * old_shnum;
- uwrite(fd_map, old_shoff + (void *)ehdr,
- sizeof(Elf_Shdr) * old_shnum);
+ uwrite(fd_map, old_shdr0, sizeof(Elf_Shdr) * old_shnum);

/* new sections __mcount_loc and .rel__mcount_loc */
t += 2*sizeof(mcsec);
@@ -244,11 +316,6 @@ static void append_func(Elf_Ehdr *const ehdr,

uwrite(fd_map, mloc0, (void *)mlocp - (void *)mloc0);
uwrite(fd_map, mrel0, (void *)mrelp - (void *)mrel0);
-
- ehdr->e_shoff = _w(new_e_shoff);
- ehdr->e_shnum = w2(2 + w2(ehdr->e_shnum)); /* {.rel,}__mcount_loc */
- ulseek(fd_map, 0, SEEK_SET);
- uwrite(fd_map, ehdr, sizeof(*ehdr));
}

static unsigned get_mcountsym(Elf_Sym const *const sym0,
@@ -415,6 +482,7 @@ static void nop_mcount(Elf_Shdr const *const relhdr,
* 2: 00000000 0 SECTION LOCAL DEFAULT 1
*/
static unsigned find_secsym_ndx(unsigned const txtndx,
+ unsigned const *const indexes,
char const *const txtname,
uint_t *const recvalp,
Elf_Shdr const *const symhdr,
@@ -428,8 +496,12 @@ static unsigned find_secsym_ndx(unsigned const txtndx,

for (symp = sym0, t = nsym; t; --t, ++symp) {
unsigned int const st_bind = ELF_ST_BIND(symp->st_info);
+ unsigned int st_shndx = 0;
+
+ if (!get_symb_shndx(symp, sym0, indexes, &st_shndx))
+ continue;

- if (txtndx == w2(symp->st_shndx)
+ if (txtndx == st_shndx
/* avoid STB_WEAK */
&& (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) {
/* function symbols on ARM have quirks, avoid them */
@@ -504,12 +576,13 @@ do_func(Elf_Ehdr *const ehdr, char const *const fname, unsigned const reltype)
{
Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff)
+ (void *)ehdr);
- unsigned const nhdr = w2(ehdr->e_shnum);
- Elf_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)];
+ unsigned const nhdr = read_shnum(ehdr, shdr0);
+ Elf_Shdr *const shstr = &shdr0[read_shstrndx(ehdr, shdr0)];
char const *const shstrtab = (char const *)(_w(shstr->sh_offset)
+ (void *)ehdr);

Elf_Shdr const *relhdr;
+ const unsigned int *indexes = NULL; /* Special section of indexes */
unsigned k;

/* Upper bound on space: assume all relevant relocs are for mcount. */
@@ -524,13 +597,22 @@ do_func(Elf_Ehdr *const ehdr, char const *const fname, unsigned const reltype)
unsigned rel_entsize = 0;
unsigned symsec_sh_link = 0;

+ /* First, find the special section of indexes if it exists */
+ for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
+ if (relhdr->sh_type == SHT_SYMTAB_SHNDX) {
+ indexes = _w(relhdr->sh_offset) + (void *)ehdr;
+ break;
+ }
+ }
+
+ /* Then, do the main work */
for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
char const *const txtname = has_rel_mcount(relhdr, shdr0,
shstrtab, fname);
if (txtname && is_mcounted_section_name(txtname)) {
uint_t recval = 0;
unsigned const recsym = find_secsym_ndx(
- w(relhdr->sh_info), txtname, &recval,
+ w(relhdr->sh_info), indexes, txtname, &recval,
&shdr0[symsec_sh_link = w(relhdr->sh_link)],
ehdr);

--
2.20.1