Re: objtool query: section start/end symbols?

From: Josh Poimboeuf
Date: Thu Jun 06 2024 - 22:41:35 EST


On Thu, Jun 06, 2024 at 03:54:34PM -0700, Linus Torvalds wrote:
> On Thu, 6 Jun 2024 at 15:19, Josh Poimboeuf <jpoimboe@xxxxxxxxxx> wrote:
> > So while I'm not yet necessarily conceding that objtool is really needed
> > here, I could work up a quick objtool patch. It would just be x86-only,
> > is that ok for the time being?
>
> Absolutely.

This adds a new objtool "--bounds" action which creates a

__sec_<secname>_{start,end}

symbol for every section.

The only testing I've done has been staring cross-eyed at the readelf
output on a defconfig kernel.

If you have CONFIG_X86_KERNEL_IBT -- which causes objtool to run on
vmlinux.o -- you can enable bounds symbol creation by adding
OBJTOOL_ARGS="--bounds" to your make cmdline:

make -j$(nproc) -s OBJTOOL_ARGS="--bounds"

Otherwise it would need to be manually added with

objtool --bounds --link vmlinux.o

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 5e21cfb7661d..e1a86981fd96 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -68,6 +68,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
static const struct option check_options[] = {
OPT_GROUP("Actions:"),
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
+ OPT_BOOLEAN('b', "bounds", &opts.bounds, "generate section start/end bounds symbols"),
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
@@ -131,7 +132,8 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[])

static bool opts_valid(void)
{
- if (opts.hack_jump_label ||
+ if (opts.bounds ||
+ opts.hack_jump_label ||
opts.hack_noinstr ||
opts.ibt ||
opts.mcount ||
@@ -199,6 +201,11 @@ static bool link_opts_valid(struct objtool_file *file)
return false;
}

+ if (opts.bounds) {
+ ERROR("--bounds requires --link");
+ return false;
+ }
+
return true;
}

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0a33d9195b7a..59e6638db83f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4213,6 +4213,124 @@ static int add_prefix_symbols(struct objtool_file *file)
return 0;
}

+static bool is_discarded_sec(struct section *sec)
+{
+ static const char * const discards[] = {
+ ".note.gnu.property",
+ ".export_symbol",
+ ".modinfo",
+ "__patchable_function_entries",
+ ".exitcall.exit", // not discarded for modules
+ };
+
+ if (!(sec->sh.sh_flags & SHF_ALLOC) || strstarts(sec->name, ".discard"))
+ return true;
+
+ for (int i = 0; i < ARRAY_SIZE(discards); i++)
+ if (!strcmp(sec->name, discards[i]))
+ return true;
+
+ return false;
+}
+
+#define BOUNDS_SEC_NAME ".rodata.sec_bounds"
+
+static int create_bounds_section(struct objtool_file *file)
+{
+ struct section *bounds_sec, *sec;
+ unsigned int idx;
+
+ sec = find_section_by_name(file->elf, BOUNDS_SEC_NAME);
+ if (sec) {
+ INIT_LIST_HEAD(&file->static_call_list);
+ WARN("file already has .sec_bounds section, skipping");
+ return 0;
+ }
+
+ idx = 0;
+ for_each_sec(file, sec)
+ if (sec->idx && !is_reloc_sec(sec) && !is_discarded_sec(sec))
+ idx++;
+
+ bounds_sec = elf_create_section_pair(file->elf, BOUNDS_SEC_NAME,
+ sizeof(long) * 2, idx, idx * 2);
+ if (!sec)
+ return -1;
+
+ idx = 0;
+ for_each_sec(file, sec) {
+ if (sec->idx && !is_reloc_sec(sec) && !is_discarded_sec(sec)) {
+
+ char sanitized_name[116];
+ char start_name[128];
+ char end_name[128];
+
+ if (!strcmp(sec->name, BOUNDS_SEC_NAME))
+ continue;
+
+ if (!sec->sym && !elf_create_section_symbol(file->elf, sec))
+ return -1;
+
+ strncpy(sanitized_name, sec->name, sizeof(sanitized_name) - 1);
+ for (char *s = sanitized_name; *s; s++)
+ if (*s == '.')
+ *s = '_';
+
+ snprintf(start_name, 256, "__sec%s_start", sanitized_name);
+ snprintf(end_name, 256, "__sec%s_end", sanitized_name);
+
+ if (find_symbol_by_name(file->elf, start_name) ||
+ find_symbol_by_name(file->elf, end_name))
+ continue;
+
+ /* 'start' symbol */
+ if (!elf_create_symbol(file->elf,
+ start_name,
+ bounds_sec,
+ STB_GLOBAL,
+ STT_OBJECT,
+ idx * 2 * sizeof(long),
+ sizeof(long)))
+ return -1;
+
+ if (!elf_create_symbol(file->elf,
+ end_name,
+ bounds_sec,
+ STB_GLOBAL,
+ STT_OBJECT,
+ (idx * 2 * sizeof(long)) + sizeof(long),
+ sizeof(long)))
+ return -1;
+
+ /* 'start' reloc */
+ if (!elf_init_reloc(file->elf,
+ bounds_sec->rsec,
+ idx * 2,
+ idx * 2 * sizeof(long),
+ sec->sym,
+ 0,
+ R_ABS64))
+ return -1;
+
+ /* 'end' reloc */
+ if (!elf_init_reloc(file->elf,
+ bounds_sec->rsec,
+ (idx * 2) + 1,
+ (idx * 2 * sizeof(long)) + sizeof(long),
+ sec->sym,
+ sec->sh.sh_size,
+ R_ABS64))
+ return -1;
+
+
+ idx++;
+ }
+ }
+
+
+ return 0;
+}
+
static int validate_symbol(struct objtool_file *file, struct section *sec,
struct symbol *sym, struct insn_state *state)
{
@@ -4826,6 +4944,13 @@ int check(struct objtool_file *file)
warnings += ret;
}

+ if (opts.bounds) {
+ ret = create_bounds_section(file);
+ if (ret < 0)
+ goto out;
+ warnings += ret;
+ }
+
if (opts.ibt) {
ret = create_ibt_endbr_seal_sections(file);
if (ret < 0)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 3d27983dc908..b596e992ca80 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -788,8 +788,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
return sym;
}

-static struct symbol *
-elf_create_section_symbol(struct elf *elf, struct section *sec)
+struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
{
struct symbol *sym = calloc(1, sizeof(*sym));

@@ -811,6 +810,8 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
if (sym)
elf_add_symbol(elf, sym);

+ sec->sym = sym;
+
return sym;
}

@@ -845,10 +846,37 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
return sym;
}

-static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
- unsigned int reloc_idx,
- unsigned long offset, struct symbol *sym,
- s64 addend, unsigned int type)
+struct symbol *elf_create_symbol(struct elf *elf, char *name,
+ struct section *sec, unsigned int bind,
+ unsigned int type, unsigned long offset,
+ size_t size)
+{
+ struct symbol *sym = calloc(1, sizeof(*sym));
+
+ if (!sym) {
+ perror("calloc");
+ return NULL;
+ }
+
+ sym->name = strdup(name);
+ sym->sec = sec;
+
+ sym->sym.st_name = elf_add_string(elf, NULL, sym->name);
+ sym->sym.st_info = GELF_ST_INFO(bind, type);
+ sym->sym.st_value = offset;
+ sym->sym.st_size = size;
+
+ sym = __elf_create_symbol(elf, sym);
+ if (sym)
+ elf_add_symbol(elf, sym);
+
+ return sym;
+}
+
+struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
+ unsigned int reloc_idx,
+ unsigned long offset, struct symbol *sym,
+ s64 addend, unsigned int type)
{
struct reloc *reloc, empty = { 0 };

@@ -906,8 +934,6 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
sym = elf_create_section_symbol(elf, insn_sec);
if (!sym)
return NULL;
-
- insn_sec->sym = sym;
}

return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index fcca6662c8b4..c0a73e247722 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -30,6 +30,7 @@ struct opts {
/* options: */
bool backtrace;
bool backup;
+ bool bounds;
bool dryrun;
bool link;
bool mnop;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index 2b8a69de4db8..a2f9c1b4ca88 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -113,8 +113,20 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
size_t entsize, unsigned int nr,
unsigned int reloc_nr);

+struct symbol *elf_create_symbol(struct elf *elf, char *name,
+ struct section *sec, unsigned int bind,
+ unsigned int type, unsigned long offset,
+ size_t size);
+
+struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);
+
struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);

+struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
+ unsigned int reloc_idx,
+ unsigned long offset, struct symbol *sym,
+ s64 addend, unsigned int type);
+
struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
unsigned long offset,
unsigned int reloc_idx,