// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
*/
#include <subcmd/parse-options.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <objtool/builtin.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
#define ORIG_SUFFIX
".orig"
int orig_argc;
static char **orig_argv;
const char *objname;
struct opts opts;
static const char *
const check_usage[] = {
"objtool [] file.o" ,
NULL,
};
static const char *
const env_usage[] = {
"OBJTOOL_ARGS=\" <options>\
"" ,
NULL,
};
static int parse_dump(
const struct option *opt,
const char *str,
int unset)
{
if (!str || !strcmp(str,
"orc" )) {
opts.dump_orc =
true ;
return 0;
}
return -1;
}
static int parse_hacks(
const struct option *opt,
const char *str,
int unset)
{
bool found =
false ;
/*
* Use strstr() as a lazy method of checking for comma-separated
* options.
*
* No string provided == enable all options.
*/
if (!str || strstr(str,
"jump_label" )) {
opts.hack_jump_label =
true ;
found =
true ;
}
if (!str || strstr(str,
"noinstr" )) {
opts.hack_noinstr =
true ;
found =
true ;
}
if (!str || strstr(str,
"skylake" )) {
opts.hack_skylake =
true ;
found =
true ;
}
return found ? 0 : -1;
}
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(
'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" ),
OPT_BOOLEAN(0,
"orc" , &opts.orc,
"generate ORC metadata" ),
OPT_BOOLEAN(
'r' ,
"retpoline" , &opts.retpoline,
"validate and annotate retpoline usage" ),
OPT_BOOLEAN(0,
"rethunk" , &opts.rethunk,
"validate and annotate rethunk usage" ),
OPT_BOOLEAN(0,
"unret" , &opts.unret,
"validate entry unret placement" ),
OPT_INTEGER(0,
"prefix" , &opts.prefix,
"generate prefix symbols" ),
OPT_BOOLEAN(
'l' ,
"sls" , &opts.sls,
"validate straight-line-speculation mitigations" ),
OPT_BOOLEAN(
's' ,
"stackval" , &opts.stackval,
"validate frame pointer rules" ),
OPT_BOOLEAN(
't' ,
"static-call" , &opts.static_call,
"annotate static calls" ),
OPT_BOOLEAN(
'u' ,
"uaccess" , &opts.uaccess,
"validate uaccess rules for SMAP" ),
OPT_BOOLEAN(0 ,
"cfi" , &opts.cfi,
"annotate kernel control flow integrity (kCFI) function preambles" ),
OPT_CALLBACK_OPTARG(0,
"dump" , NULL, NULL,
"orc" ,
"dump metadata" , parse_dump),
OPT_GROUP(
"Options:" ),
OPT_BOOLEAN(0,
"backtrace" , &opts.backtrace,
"unwind on error" ),
OPT_BOOLEAN(0,
"dry-run" , &opts.dryrun,
"don't write modifications" ),
OPT_BOOLEAN(0,
"link" , &opts.link,
"object is a linked object" ),
OPT_BOOLEAN(0,
"module" , &opts.module,
"object is part of a kernel module" ),
OPT_BOOLEAN(0,
"mnop" , &opts.mnop,
"nop out mcount call sites" ),
OPT_BOOLEAN(0,
"no-unreachable" , &opts.no_unreachable,
"skip 'unreachable instruction' warnings" ),
OPT_STRING(
'o' ,
"output" , &opts.output,
"file" ,
"output file name" ),
OPT_BOOLEAN(0,
"sec-address" , &opts.sec_address,
"print section addresses in warnings" ),
OPT_BOOLEAN(0,
"stats" , &opts.stats,
"print statistics" ),
OPT_BOOLEAN(
'v' ,
"verbose" , &opts.verbose,
"verbose warnings" ),
OPT_BOOLEAN(0,
"Werror" , &opts.werror,
"return error on warnings" ),
OPT_END(),
};
int cmd_parse_options(
int argc,
const char **argv,
const char *
const usage[])
{
const char *envv[16] = { };
char *env;
int envc;
env = getenv(
"OBJTOOL_ARGS" );
if (env) {
envv[0] =
"OBJTOOL_ARGS" ;
for (envc = 1; envc < ARRAY_SIZE(envv); ) {
envv[envc++] = env;
env = strchr(env,
' ' );
if (!env)
break ;
*env =
'\0' ;
env++;
}
parse_options(envc, envv, check_options, env_usage, 0);
}
env = getenv(
"OBJTOOL_VERBOSE" );
if (env && !strcmp(env,
"1" ))
opts.verbose =
true ;
argc = parse_options(argc, argv, check_options, usage, 0);
if (argc != 1)
usage_with_options(usage, check_options);
return argc;
}
static bool opts_valid(
void )
{
if (opts.mnop && !opts.mcount) {
ERROR(
"--mnop requires --mcount" );
return false ;
}
if (opts.noinstr && !opts.link) {
ERROR(
"--noinstr requires --link" );
return false ;
}
if (opts.ibt && !opts.link) {
ERROR(
"--ibt requires --link" );
return false ;
}
if (opts.unret && !opts.link) {
ERROR(
"--unret requires --link" );
return false ;
}
if (opts.hack_jump_label ||
opts.hack_noinstr ||
opts.ibt ||
opts.mcount ||
opts.noinstr ||
opts.orc ||
opts.retpoline ||
opts.rethunk ||
opts.sls ||
opts.stackval ||
opts.static_call ||
opts.uaccess) {
if (opts.dump_orc) {
ERROR(
"--dump can't be combined with other actions" );
return false ;
}
return true ;
}
if (opts.dump_orc)
return true ;
ERROR(
"At least one action required" );
return false ;
}
static int copy_file(
const char *src,
const char *dst)
{
size_t to_copy, copied;
int dst_fd, src_fd;
struct stat stat;
off_t offset = 0;
src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
ERROR(
"can't open %s for reading: %s" , src, strerror(errno));
return 1;
}
dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (dst_fd == -1) {
ERROR(
"can't open %s for writing: %s" , dst, strerror(errno));
return 1;
}
if (fstat(src_fd, &stat) == -1) {
ERROR_GLIBC(
"fstat" );
return 1;
}
if (fchmod(dst_fd, stat.st_mode) == -1) {
ERROR_GLIBC(
"fchmod" );
return 1;
}
for (to_copy = stat.st_size; to_copy > 0; to_copy -= copied) {
copied = sendfile(dst_fd, src_fd, &offset, to_copy);
if (copied == -1) {
ERROR_GLIBC(
"sendfile" );
return 1;
}
}
close(dst_fd);
close(src_fd);
return 0;
}
static void save_argv(
int argc,
const char **argv)
{
orig_argv = calloc(argc,
sizeof (
char *));
if (!orig_argv) {
ERROR_GLIBC(
"calloc" );
exit (1);
}
for (
int i = 0; i < argc; i++) {
orig_argv[i] = strdup(argv[i]);
if (!orig_argv[i]) {
ERROR_GLIBC(
"strdup(%s)" , argv[i]);
exit (1);
}
};
}
void print_args(
void )
{
char *backup = NULL;
if (opts.output || opts.dryrun)
goto print;
/*
* Make a backup before kbuild deletes the file so the error
* can be recreated without recompiling or relinking.
*/
backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1);
if (!backup) {
ERROR_GLIBC(
"malloc" );
goto print;
}
strcpy(backup, objname);
strcat(backup, ORIG_SUFFIX);
if (copy_file(objname, backup)) {
backup = NULL;
goto print;
}
print:
/*
* Print the cmdline args to make it easier to recreate. If '--output'
* wasn't used, add it to the printed args with the backup as input.
*/
fprintf(stderr,
"%s" , orig_argv[0]);
for (
int i = 1; i < orig_argc; i++) {
char *arg = orig_argv[i];
if (backup && !strcmp(arg, objname))
fprintf(stderr,
" %s -o %s" , backup, objname);
else
fprintf(stderr,
" %s" , arg);
}
fprintf(stderr,
"\n" );
}
int objtool_run(
int argc,
const char **argv)
{
struct objtool_file *file;
int ret = 0;
orig_argc = argc;
save_argv(argc, argv);
cmd_parse_options(argc, argv, check_usage);
if (!opts_valid())
return 1;
objname = argv[0];
if (opts.dump_orc)
return orc_dump(objname);
if (!opts.dryrun && opts.output) {
/* copy original .o file to output file */
if (copy_file(objname, opts.output))
return 1;
/* from here on, work directly on the output file */
objname = opts.output;
}
file = objtool_open_read(objname);
if (!file)
return 1;
if (!opts.link && has_multiple_files(file->elf)) {
ERROR(
"Linked object requires --link" );
return 1;
}
ret = check(file);
if (ret)
return ret;
if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
return 1;
return 0;
}
Messung V0.5 C=90 H=97 G=93
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland