/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* objtool check:
*
* This command analyzes every .o file and ensures the validity of its stack
* trace metadata. It enforces a set of rules on asm code and C inline
* assembly code so that stack traces can be reliable.
*
* For more information, see tools/objtool/Documentation/stack-validation.txt.
*/
#include <string.h>
#include <subcmd/parse-options.h>
#include "builtin.h"
#include "elf.h"
#include "special.h"
#include "arch.h"
#include "warn.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define STATE_FP_SAVED 0x1
#define STATE_FP_SETUP 0x2
#define STATE_FENTRY 0x4
struct instruction {
struct list_head list;
struct section *sec;
unsigned long offset;
unsigned int len, state;
unsigned char type;
unsigned long immediate;
bool alt_group, visited;
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
};
struct alternative {
struct list_head list;
struct instruction *insn;
};
struct objtool_file {
struct elf *elf;
struct list_head insns;
};
const char *objname;
static bool nofp;
static struct instruction *find_instruction(struct objtool_file *file,
struct section *sec,
unsigned long offset)
{
struct instruction *insn;
list_for_each_entry(insn, &file->insns, list)
if (insn->sec == sec && insn->offset == offset)
return insn;
return NULL;
}
/*
* Check if the function has been manually whitelisted with the
* STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
* due to its use of a context switching instruction.
*/
static bool ignore_func(struct objtool_file *file, struct symbol *func)
{
struct section *macro_sec;
struct rela *rela;
struct instruction *insn;
/* check for STACK_FRAME_NON_STANDARD */
macro_sec = find_section_by_name(file->elf, "__func_stack_frame_non_standard");
if (macro_sec && macro_sec->rela)
list_for_each_entry(rela, ¯o_sec->rela->relas, list)
if (rela->sym->sec == func->sec &&
rela->addend == func->offset)
return true;
/* check if it has a context switching instruction */
insn = find_instruction(file, func->sec, func->offset);
if (!insn)
return false;
list_for_each_entry_from(insn, &file->insns, list) {
if (insn->sec != func->sec ||
insn->offset >= func->offset + func->len)
break;
if (insn->type == INSN_CONTEXT_SWITCH)
return true;
}
return false;
}
/*
* This checks to see if the given function is a "noreturn" function.
*
* For global functions which are outside the scope of this object file, we
* have to keep a manual list of them.
*
* For local functions, we have to detect them manually by simply looking for
* the lack of a return instruction.
*
* Returns:
* -1: error
* 0: no dead end
* 1: dead end
*/
static int __dead_end_function(struct objtool_file *file, struct symbol *func,
int recursion)
{
int i;
struct instruction *insn, *func_insn;
bool empty = true;
/*
* Unfortunately these have to be hard coded because the noreturn
* attribute isn't provided in ELF data.
*/
static const char * const global_noreturns[] = {
"__stack_chk_fail",
"panic",
"do_exit",
"__module_put_and_exit",
"complete_and_exit",
"kvm_spurious_fault",
"__reiserfs_panic",
"lbug_with_loc"
};
if (func->bind<