linux-stable/arch/x86/tools/insn_decoder_test.c
Sergio González Collado c104c16073 Kunit to check the longest symbol length
The longest length of a symbol (KSYM_NAME_LEN) was increased to 512
in the reference [1]. This patch adds kunit test suite to check the longest
symbol length. These tests verify that the longest symbol length defined
is supported.

This test can also help other efforts for longer symbol length,
like [2].

The test suite defines one symbol with the longest possible length.

The first test verify that functions with names of the created
symbol, can be called or not.

The second test, verify that the symbols are created (or
not) in the kernel symbol table.

[1] https://lore.kernel.org/lkml/20220802015052.10452-6-ojeda@kernel.org/
[2] https://lore.kernel.org/lkml/20240605032120.3179157-1-song@kernel.org/

Link: https://lore.kernel.org/r/20250302221518.76874-1-sergio.collado@gmail.com
Tested-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>
Reviewed-by: Rae Moar <rmoar@google.com>
Signed-off-by: Sergio González Collado <sergio.collado@gmail.com>
Link: https://github.com/Rust-for-Linux/linux/issues/504
Reviewed-by: Rae Moar <rmoar@google.com>
Acked-by: David Gow <davidgow@google.com>
Signed-off-by: Shuah Khan <shuah@kernel.org>
2025-03-15 18:13:31 -06:00

176 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Copyright (C) IBM Corporation, 2009
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
#include <linux/kallsyms.h>
#define unlikely(cond) (cond)
#include <asm/insn.h>
#include <inat.c>
#include <insn.c>
/*
* Test of instruction analysis in general and insn_get_length() in
* particular. See if insn_get_length() and the disassembler agree
* on the length of each instruction in an elf disassembly.
*
* Usage: objdump -d a.out | awk -f objdump_reformat.awk | ./insn_decoder_test
*/
const char *prog;
static int verbose;
static int x86_64;
static void usage(void)
{
fprintf(stderr, "Usage: objdump -d a.out | awk -f objdump_reformat.awk"
" | %s [-y|-n] [-v]\n", prog);
fprintf(stderr, "\t-y 64bit mode\n");
fprintf(stderr, "\t-n 32bit mode\n");
fprintf(stderr, "\t-v verbose mode\n");
exit(1);
}
static void malformed_line(const char *line, int line_nr)
{
fprintf(stderr, "%s: error: malformed line %d:\n%s",
prog, line_nr, line);
exit(3);
}
static void pr_warn(const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s: warning: ", prog);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static void dump_field(FILE *fp, const char *name, const char *indent,
struct insn_field *field)
{
fprintf(fp, "%s.%s = {\n", indent, name);
fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n",
indent, field->value, field->bytes[0], field->bytes[1],
field->bytes[2], field->bytes[3]);
fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent,
field->got, field->nbytes);
}
static void dump_insn(FILE *fp, struct insn *insn)
{
fprintf(fp, "Instruction = {\n");
dump_field(fp, "prefixes", "\t", &insn->prefixes);
dump_field(fp, "rex_prefix", "\t", &insn->rex_prefix);
dump_field(fp, "vex_prefix", "\t", &insn->vex_prefix);
dump_field(fp, "opcode", "\t", &insn->opcode);
dump_field(fp, "modrm", "\t", &insn->modrm);
dump_field(fp, "sib", "\t", &insn->sib);
dump_field(fp, "displacement", "\t", &insn->displacement);
dump_field(fp, "immediate1", "\t", &insn->immediate1);
dump_field(fp, "immediate2", "\t", &insn->immediate2);
fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n",
insn->attr, insn->opnd_bytes, insn->addr_bytes);
fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n",
insn->length, insn->x86_64, insn->kaddr);
}
static void parse_args(int argc, char **argv)
{
int c;
prog = argv[0];
while ((c = getopt(argc, argv, "ynv")) != -1) {
switch (c) {
case 'y':
x86_64 = 1;
break;
case 'n':
x86_64 = 0;
break;
case 'v':
verbose = 1;
break;
default:
usage();
}
}
}
#define BUFSIZE (256 + KSYM_NAME_LEN)
int main(int argc, char **argv)
{
char line[BUFSIZE], sym[BUFSIZE] = "<unknown>";
unsigned char insn_buff[16];
struct insn insn;
int insns = 0;
int warnings = 0;
parse_args(argc, argv);
while (fgets(line, BUFSIZE, stdin)) {
char copy[BUFSIZE], *s, *tab1, *tab2;
int nb = 0, ret;
unsigned int b;
if (line[0] == '<') {
/* Symbol line */
strcpy(sym, line);
continue;
}
insns++;
memset(insn_buff, 0, 16);
strcpy(copy, line);
tab1 = strchr(copy, '\t');
if (!tab1)
malformed_line(line, insns);
s = tab1 + 1;
s += strspn(s, " ");
tab2 = strchr(s, '\t');
if (!tab2)
malformed_line(line, insns);
*tab2 = '\0'; /* Characters beyond tab2 aren't examined */
while (s < tab2) {
if (sscanf(s, "%x", &b) == 1) {
insn_buff[nb++] = (unsigned char) b;
s += 3;
} else
break;
}
/* Decode an instruction */
ret = insn_decode(&insn, insn_buff, sizeof(insn_buff),
x86_64 ? INSN_MODE_64 : INSN_MODE_32);
if (ret < 0 || insn.length != nb) {
warnings++;
pr_warn("Found an x86 instruction decoder bug, "
"please report this.\n", sym);
pr_warn("%s", line);
pr_warn("objdump says %d bytes, but insn_get_length() "
"says %d\n", nb, insn.length);
if (verbose)
dump_insn(stderr, &insn);
}
}
if (warnings)
pr_warn("Decoded and checked %d instructions with %d "
"failures\n", insns, warnings);
else
fprintf(stdout, "%s: success: Decoded and checked %d"
" instructions\n", prog, insns);
return 0;
}