diff options
author | Josh Poimboeuf <jpoimboe@kernel.org> | 2025-03-14 12:29:07 -0700 |
---|---|---|
committer | Peter Zijlstra <peterz@infradead.org> | 2025-03-17 11:36:01 +0100 |
commit | 5a406031d0719d146d2033ee4270310b1ca9a1e3 (patch) | |
tree | 4df3c743470970860b84a076187c45bbf9357697 /tools/objtool | |
parent | fdf5ff2934f4c5c6b483c906fea6e0288df36da2 (diff) |
objtool: Add --output option
Add option to allow writing the changed binary to a separate file rather
than changing it in place.
Libelf makes this suprisingly hard, so take the easy way out and just
copy the file before editing it.
Also steal the -o short option from --orc. Nobody will notice ;-)
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/0da308d42d82b3bbed16a31a72d6bde52afcd6bd.1741975349.git.jpoimboe@kernel.org
Diffstat (limited to 'tools/objtool')
-rw-r--r-- | tools/objtool/builtin-check.c | 98 | ||||
-rw-r--r-- | tools/objtool/elf.c | 3 | ||||
-rw-r--r-- | tools/objtool/include/objtool/builtin.h | 1 | ||||
-rw-r--r-- | tools/objtool/objtool.c | 15 | ||||
-rw-r--r-- | tools/objtool/orc_dump.c | 7 |
5 files changed, 89 insertions, 35 deletions
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 79843512a51b..3de3afa0a19c 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -6,6 +6,10 @@ #include <subcmd/parse-options.h> #include <string.h> #include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/sendfile.h> #include <objtool/builtin.h> #include <objtool/objtool.h> @@ -14,6 +18,8 @@ "error: objtool: " format "\n", \ ##__VA_ARGS__) +const char *objname; + struct opts opts; static const char * const check_usage[] = { @@ -71,7 +77,7 @@ static const struct option check_options[] = { 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('o', "orc", &opts.orc, "generate ORC metadata"), + 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"), @@ -84,15 +90,16 @@ static const struct option check_options[] = { 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, "backup", &opts.backup, "create .orig files before modification"), - 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_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), - OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), + OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"), + OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"), + 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_END(), @@ -178,24 +185,75 @@ static bool opts_valid(void) 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", src); + return 1; + } + + dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC); + if (dst_fd == -1) { + ERROR("can't open '%s' for writing", dst); + return 1; + } + + if (fstat(src_fd, &stat) == -1) { + perror("fstat"); + return 1; + } + + if (fchmod(dst_fd, stat.st_mode) == -1) { + perror("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) { + perror("sendfile"); + return 1; + } + } + + close(dst_fd); + close(src_fd); + return 0; +} + int objtool_run(int argc, const char **argv) { - const char *objname; struct objtool_file *file; int ret; - argc = cmd_parse_options(argc, argv, check_usage); - objname = argv[0]; + 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; + goto err; if (!opts.link && has_multiple_files(file->elf)) { ERROR("Linked object requires --link"); @@ -204,10 +262,16 @@ int objtool_run(int argc, const char **argv) ret = check(file); if (ret) - return ret; + goto err; - if (file->elf->changed) - return elf_write(file->elf); + if (!opts.dryrun && file->elf->changed && elf_write(file->elf)) + goto err; return 0; + +err: + if (opts.output) + unlink(opts.output); + + return 1; } diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 6f64d611faea..be4f4b62730c 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -1302,9 +1302,6 @@ int elf_write(struct elf *elf) struct section *sec; Elf_Scn *s; - if (opts.dryrun) - return 0; - /* Update changed relocation sections and section headers: */ list_for_each_entry(sec, &elf->sections, list) { if (sec->truncate) diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index fcca6662c8b4..25cfa01758b9 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -35,6 +35,7 @@ struct opts { bool mnop; bool module; bool no_unreachable; + const char *output; bool sec_address; bool stats; bool verbose; diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index f40febdd6e36..53cd881c6b95 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -18,7 +18,6 @@ bool help; -const char *objname; static struct objtool_file file; static bool objtool_create_backup(const char *_objname) @@ -79,18 +78,14 @@ static bool objtool_create_backup(const char *_objname) return true; } -struct objtool_file *objtool_open_read(const char *_objname) +struct objtool_file *objtool_open_read(const char *filename) { - if (objname) { - if (strcmp(objname, _objname)) { - WARN("won't handle more than one file at a time"); - return NULL; - } - return &file; + if (file.elf) { + WARN("won't handle more than one file at a time"); + return NULL; } - objname = _objname; - file.elf = elf_open_read(objname, O_RDWR); + file.elf = elf_open_read(filename, O_RDWR); if (!file.elf) return NULL; diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index a62247efb64f..05ef0e297837 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -10,7 +10,7 @@ #include <objtool/warn.h> #include <objtool/endianness.h> -int orc_dump(const char *_objname) +int orc_dump(const char *filename) { int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; struct orc_entry *orc = NULL; @@ -26,12 +26,9 @@ int orc_dump(const char *_objname) Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; struct elf dummy_elf = {}; - - objname = _objname; - elf_version(EV_CURRENT); - fd = open(objname, O_RDONLY); + fd = open(filename, O_RDONLY); if (fd == -1) { perror("open"); return -1; |