@@ -129,16 +129,13 @@ static bool is_jump_table_jump(struct instruction *insn)
129
129
static bool is_sibling_call (struct instruction * insn )
130
130
{
131
131
/*
132
- * Assume only ELF functions can make sibling calls. This ensures
133
- * sibling call detection consistency between vmlinux.o and individual
134
- * objects.
132
+ * Assume only STT_FUNC calls have jump-tables.
135
133
*/
136
- if (!insn_func (insn ))
137
- return false;
138
-
139
- /* An indirect jump is either a sibling call or a jump to a table. */
140
- if (insn -> type == INSN_JUMP_DYNAMIC )
141
- return !is_jump_table_jump (insn );
134
+ if (insn_func (insn )) {
135
+ /* An indirect jump is either a sibling call or a jump to a table. */
136
+ if (insn -> type == INSN_JUMP_DYNAMIC )
137
+ return !is_jump_table_jump (insn );
138
+ }
142
139
143
140
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
144
141
return (is_static_jump (insn ) && insn -> call_dest );
@@ -1400,27 +1397,50 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn,
1400
1397
list_add_tail (& insn -> call_node , & file -> return_thunk_list );
1401
1398
}
1402
1399
1403
- static bool same_function (struct instruction * insn1 , struct instruction * insn2 )
1404
- {
1405
- return insn_func (insn1 )-> pfunc == insn_func (insn2 )-> pfunc ;
1406
- }
1407
-
1408
- static bool is_first_func_insn (struct objtool_file * file , struct instruction * insn )
1400
+ static bool is_first_func_insn (struct objtool_file * file ,
1401
+ struct instruction * insn , struct symbol * sym )
1409
1402
{
1410
- if (insn -> offset == insn_func ( insn ) -> offset )
1403
+ if (insn -> offset == sym -> offset )
1411
1404
return true;
1412
1405
1406
+ /* Allow direct CALL/JMP past ENDBR */
1413
1407
if (opts .ibt ) {
1414
1408
struct instruction * prev = prev_insn_same_sym (file , insn );
1415
1409
1416
1410
if (prev && prev -> type == INSN_ENDBR &&
1417
- insn -> offset == insn_func ( insn ) -> offset + prev -> len )
1411
+ insn -> offset == sym -> offset + prev -> len )
1418
1412
return true;
1419
1413
}
1420
1414
1421
1415
return false;
1422
1416
}
1423
1417
1418
+ /*
1419
+ * A sibling call is a tail-call to another symbol -- to differentiate from a
1420
+ * recursive tail-call which is to the same symbol.
1421
+ */
1422
+ static bool jump_is_sibling_call (struct objtool_file * file ,
1423
+ struct instruction * from , struct instruction * to )
1424
+ {
1425
+ struct symbol * fs = from -> sym ;
1426
+ struct symbol * ts = to -> sym ;
1427
+
1428
+ /* Not a sibling call if from/to a symbol hole */
1429
+ if (!fs || !ts )
1430
+ return false;
1431
+
1432
+ /* Not a sibling call if not targeting the start of a symbol. */
1433
+ if (!is_first_func_insn (file , to , ts ))
1434
+ return false;
1435
+
1436
+ /* Disallow sibling calls into STT_NOTYPE */
1437
+ if (ts -> type == STT_NOTYPE )
1438
+ return false;
1439
+
1440
+ /* Must not be self to be a sibling */
1441
+ return fs -> pfunc != ts -> pfunc ;
1442
+ }
1443
+
1424
1444
/*
1425
1445
* Find the destination instructions for all jumps.
1426
1446
*/
@@ -1519,18 +1539,18 @@ static int add_jump_destinations(struct objtool_file *file)
1519
1539
strstr (insn_func (jump_dest )-> name , ".cold" )) {
1520
1540
insn_func (insn )-> cfunc = insn_func (jump_dest );
1521
1541
insn_func (jump_dest )-> pfunc = insn_func (insn );
1522
-
1523
- } else if (!same_function (insn , jump_dest ) &&
1524
- is_first_func_insn (file , jump_dest )) {
1525
- /*
1526
- * Internal sibling call without reloc or with
1527
- * STT_SECTION reloc.
1528
- */
1529
- add_call_dest (file , insn , insn_func (jump_dest ), true);
1530
- continue ;
1531
1542
}
1532
1543
}
1533
1544
1545
+ if (jump_is_sibling_call (file , insn , jump_dest )) {
1546
+ /*
1547
+ * Internal sibling call without reloc or with
1548
+ * STT_SECTION reloc.
1549
+ */
1550
+ add_call_dest (file , insn , insn_func (jump_dest ), true);
1551
+ continue ;
1552
+ }
1553
+
1534
1554
insn -> jump_dest = jump_dest ;
1535
1555
}
1536
1556
@@ -3309,7 +3329,7 @@ static int validate_sibling_call(struct objtool_file *file,
3309
3329
struct instruction * insn ,
3310
3330
struct insn_state * state )
3311
3331
{
3312
- if (has_modified_stack_frame (insn , state )) {
3332
+ if (insn_func ( insn ) && has_modified_stack_frame (insn , state )) {
3313
3333
WARN_FUNC ("sibling call from callable instruction with modified stack frame" ,
3314
3334
insn -> sec , insn -> offset );
3315
3335
return 1 ;
0 commit comments