Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

objtool: Add support for intra-function calls

Change objtool to support intra-function calls. On x86, an intra-function
call is represented in objtool as a push onto the stack (of the return
address), and a jump to the destination address. That way the stack
information is correctly updated and the call flow is still accurate.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200414103618.12657-4-alexandre.chartre@oracle.com

authored by

Alexandre Chartre and committed by
Peter Zijlstra
8aa8eb2a b490f453

+102 -4
+11
include/linux/frame.h
··· 15 15 static void __used __section(.discard.func_stack_frame_non_standard) \ 16 16 *__func_stack_frame_non_standard_##func = func 17 17 18 + /* 19 + * This macro indicates that the following intra-function call is valid. 20 + * Any non-annotated intra-function call will cause objtool to issue a warning. 21 + */ 22 + #define ANNOTATE_INTRA_FUNCTION_CALL \ 23 + 999: \ 24 + .pushsection .discard.intra_function_calls; \ 25 + .long 999b; \ 26 + .popsection; 27 + 18 28 #else /* !CONFIG_STACK_VALIDATION */ 19 29 20 30 #define STACK_FRAME_NON_STANDARD(func) 31 + #define ANNOTATE_INTRA_FUNCTION_CALL 21 32 22 33 #endif /* CONFIG_STACK_VALIDATION */ 23 34
+8
tools/objtool/Documentation/stack-validation.txt
··· 323 323 The easiest way to enforce this is to ensure alternatives do not contain 324 324 any ORC entries, which in turn implies the above constraint. 325 325 326 + 11. file.o: warning: unannotated intra-function call 327 + 328 + This warning means that a direct call is done to a destination which 329 + is not at the beginning of a function. If this is a legit call, you 330 + can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL 331 + directive right before the call. 332 + 333 + 326 334 If the error doesn't seem to make sense, it could be a bug in objtool. 327 335 Feel free to ask the objtool maintainer for help. 328 336
+8
tools/objtool/arch/x86/decode.c
··· 496 496 497 497 case 0xe8: 498 498 *type = INSN_CALL; 499 + /* 500 + * For the impact on the stack, a CALL behaves like 501 + * a PUSH of an immediate value (the return address). 502 + */ 503 + ADD_OP(op) { 504 + op->src.type = OP_SRC_CONST; 505 + op->dest.type = OP_DEST_PUSH; 506 + } 499 507 break; 500 508 501 509 case 0xfc:
+75 -4
tools/objtool/check.c
··· 674 674 return 0; 675 675 } 676 676 677 + static void remove_insn_ops(struct instruction *insn) 678 + { 679 + struct stack_op *op, *tmp; 680 + 681 + list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) { 682 + list_del(&op->list); 683 + free(op); 684 + } 685 + } 686 + 677 687 /* 678 688 * Find the destination instructions for all calls. 679 689 */ ··· 709 699 continue; 710 700 711 701 if (!insn->call_dest) { 712 - WARN_FUNC("unsupported intra-function call", 713 - insn->sec, insn->offset); 714 - if (retpoline) 715 - WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); 702 + WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset); 716 703 return -1; 717 704 } 718 705 ··· 732 725 } 733 726 } else 734 727 insn->call_dest = rela->sym; 728 + 729 + /* 730 + * Whatever stack impact regular CALLs have, should be undone 731 + * by the RETURN of the called function. 732 + * 733 + * Annotated intra-function calls retain the stack_ops but 734 + * are converted to JUMP, see read_intra_function_calls(). 735 + */ 736 + remove_insn_ops(insn); 735 737 } 736 738 737 739 return 0; ··· 1420 1404 return 0; 1421 1405 } 1422 1406 1407 + static int read_intra_function_calls(struct objtool_file *file) 1408 + { 1409 + struct instruction *insn; 1410 + struct section *sec; 1411 + struct rela *rela; 1412 + 1413 + sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); 1414 + if (!sec) 1415 + return 0; 1416 + 1417 + list_for_each_entry(rela, &sec->rela_list, list) { 1418 + unsigned long dest_off; 1419 + 1420 + if (rela->sym->type != STT_SECTION) { 1421 + WARN("unexpected relocation symbol type in %s", 1422 + sec->name); 1423 + return -1; 1424 + } 1425 + 1426 + insn = find_insn(file, rela->sym->sec, rela->addend); 1427 + if (!insn) { 1428 + WARN("bad .discard.intra_function_call entry"); 1429 + return -1; 1430 + } 1431 + 1432 + if (insn->type != INSN_CALL) { 1433 + WARN_FUNC("intra_function_call not a direct call", 1434 + insn->sec, insn->offset); 1435 + return -1; 1436 + } 1437 + 1438 + /* 1439 + * Treat intra-function CALLs as JMPs, but with a stack_op. 1440 + * See add_call_destinations(), which strips stack_ops from 1441 + * normal CALLs. 1442 + */ 1443 + insn->type = INSN_JUMP_UNCONDITIONAL; 1444 + 1445 + dest_off = insn->offset + insn->len + insn->immediate; 1446 + insn->jump_dest = find_insn(file, insn->sec, dest_off); 1447 + if (!insn->jump_dest) { 1448 + WARN_FUNC("can't find call dest at %s+0x%lx", 1449 + insn->sec, insn->offset, 1450 + insn->sec->name, dest_off); 1451 + return -1; 1452 + } 1453 + } 1454 + 1455 + return 0; 1456 + } 1457 + 1423 1458 static void mark_rodata(struct objtool_file *file) 1424 1459 { 1425 1460 struct section *sec; ··· 1523 1456 return ret; 1524 1457 1525 1458 ret = add_special_section_alts(file); 1459 + if (ret) 1460 + return ret; 1461 + 1462 + ret = read_intra_function_calls(file); 1526 1463 if (ret) 1527 1464 return ret; 1528 1465