Re: objtool warnings in prerelease clang-9

From: Josh Poimboeuf
Date: Wed Jul 10 2019 - 19:22:50 EST


On Sat, Jul 06, 2019 at 10:50:01AM -0500, Josh Poimboeuf wrote:
> On Tue, Jul 02, 2019 at 11:58:27PM +0200, Thomas Gleixner wrote:
> > platform-quirks.o:
> >
> > if (x86_platform.set_legacy_features)
> > 74: 4c 8b 1d 00 00 00 00 mov 0x0(%rip),%r11 # 7b <x86_early_init_platform_quirks+0x7b>
> > 7b: 4d 85 db test %r11,%r11
> > 7e: 0f 85 00 00 00 00 jne 84 <x86_early_init_platform_quirks+0x84>
> > x86_platform.set_legacy_features();
> > }
> > 84: c3 retq
> >
> > That jne jumps to __x86_indirect_thunk_r11, aka. ratpoutine.
> >
> > No idea why objtool thinks that the instruction at 0x84 is not
> > reachable. Josh?
>
> That's a conditional tail call, which is something GCC never does.
> Objtool doesn't understand that, so we'll need to fix it.

Can somebody test this patch to see if it fixes the platform-quirks.o
warning?

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 27818a93f0b1..42fe09a93593 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -97,6 +97,36 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
for (insn = next_insn_same_sec(file, insn); insn; \
insn = next_insn_same_sec(file, insn))

+static bool is_sibling_call(struct instruction *insn)
+{
+ struct symbol *insn_func, *dest_func;
+
+ /* Only C code does sibling calls. */
+ if (!insn->func)
+ return false;
+
+ /* An indirect jump is either a sibling call or a jump to a table. */
+ if (insn->type == INSN_JUMP_DYNAMIC)
+ return list_empty(&insn->alts);
+
+ if (insn->type != INSN_JUMP_CONDITIONAL &&
+ insn->type != INSN_JUMP_UNCONDITIONAL)
+ return false;
+
+ /* A direct jump to outside the .o file is a sibling call. */
+ if (!insn->jump_dest)
+ return true;
+
+ if (!insn->jump_dest->func)
+ return false;
+
+ /* A direct jump to the beginning of a function is a sibling call. */
+ insn_func = insn->func->pfunc;
+ dest_func = insn->jump_dest->func->pfunc;
+ return insn_func != dest_func &&
+ insn->jump_dest->offset == dest_func->offset;
+}
+
/*
* This checks to see if the given function is a "noreturn" function.
*
@@ -169,34 +199,25 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
* of the sibling call returns.
*/
func_for_each_insn_all(file, func, insn) {
- if (insn->type == INSN_JUMP_UNCONDITIONAL) {
+ if (is_sibling_call(insn)) {
struct instruction *dest = insn->jump_dest;

if (!dest)
/* sibling call to another file */
return 0;

- if (dest->func && dest->func->pfunc != insn->func->pfunc) {
-
- /* local sibling call */
- if (recursion == 5) {
- /*
- * Infinite recursion: two functions
- * have sibling calls to each other.
- * This is a very rare case. It means
- * they aren't dead ends.
- */
- return 0;
- }
-
- return __dead_end_function(file, dest->func,
- recursion + 1);
+ /* local sibling call */
+ if (recursion == 5) {
+ /*
+ * Infinite recursion: two functions have
+ * sibling calls to each other. This is a very
+ * rare case. It means they aren't dead ends.
+ */
+ return 0;
}
- }

- if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
- /* sibling call */
- return 0;
+ return __dead_end_function(file, dest->func, recursion+1);
+ }
}

return 1;
@@ -635,7 +656,7 @@ static int add_jump_destinations(struct objtool_file *file)
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
insn->jump_dest->offset == insn->jump_dest->func->offset) {

- /* sibling class */
+ /* sibling calls */
insn->call_dest = insn->jump_dest->func;
insn->jump_dest = NULL;
}
@@ -2100,7 +2121,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,

case INSN_JUMP_CONDITIONAL:
case INSN_JUMP_UNCONDITIONAL:
- if (func && !insn->jump_dest) {
+ if (is_sibling_call(insn)) {
ret = validate_sibling_call(insn, &state);
if (ret)
return ret;
@@ -2123,7 +2144,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
break;

case INSN_JUMP_DYNAMIC:
- if (func && list_empty(&insn->alts)) {
+ if (is_sibling_call(insn)) {
ret = validate_sibling_call(insn, &state);
if (ret)
return ret;