mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	objtool: Handle __sanitize_cov*() tail calls
Turns out the compilers also generate tail calls to __sanitize_cov*(),
make sure to also patch those out in noinstr code.
Fixes: 0f1441b44e ("objtool: Fix noinstr vs KCOV")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Marco Elver <elver@google.com>
Link: https://lore.kernel.org/r/20210624095147.818783799@infradead.org
			
			
This commit is contained in:
		
							parent
							
								
									8b946cc38e
								
							
						
					
					
						commit
						f56dae88a8
					
				
					 3 changed files with 105 additions and 74 deletions
				
			
		| 
						 | 
					@ -659,6 +659,26 @@ const char *arch_nop_insn(int len)
 | 
				
			||||||
	return nops[len-1];
 | 
						return nops[len-1];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BYTE_RET	0xC3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *arch_ret_insn(int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static const char ret[5][5] = {
 | 
				
			||||||
 | 
							{ BYTE_RET },
 | 
				
			||||||
 | 
							{ BYTE_RET, BYTES_NOP1 },
 | 
				
			||||||
 | 
							{ BYTE_RET, BYTES_NOP2 },
 | 
				
			||||||
 | 
							{ BYTE_RET, BYTES_NOP3 },
 | 
				
			||||||
 | 
							{ BYTE_RET, BYTES_NOP4 },
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len < 1 || len > 5) {
 | 
				
			||||||
 | 
							WARN("invalid RET size: %d\n", len);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret[len-1];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* asm/alternative.h ? */
 | 
					/* asm/alternative.h ? */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ALTINSTR_FLAG_INV	(1 << 15)
 | 
					#define ALTINSTR_FLAG_INV	(1 << 15)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -904,6 +904,79 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i
 | 
				
			||||||
	return insn->reloc;
 | 
						return insn->reloc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void remove_insn_ops(struct instruction *insn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stack_op *op, *tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
 | 
				
			||||||
 | 
							list_del(&op->list);
 | 
				
			||||||
 | 
							free(op);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void add_call_dest(struct objtool_file *file, struct instruction *insn,
 | 
				
			||||||
 | 
								  struct symbol *dest, bool sibling)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct reloc *reloc = insn_reloc(file, insn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						insn->call_dest = dest;
 | 
				
			||||||
 | 
						if (!dest)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (insn->call_dest->static_call_tramp) {
 | 
				
			||||||
 | 
							list_add_tail(&insn->call_node,
 | 
				
			||||||
 | 
								      &file->static_call_list);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Many compilers cannot disable KCOV with a function attribute
 | 
				
			||||||
 | 
						 * so they need a little help, NOP out any KCOV calls from noinstr
 | 
				
			||||||
 | 
						 * text.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (insn->sec->noinstr &&
 | 
				
			||||||
 | 
						    !strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
 | 
				
			||||||
 | 
							if (reloc) {
 | 
				
			||||||
 | 
								reloc->type = R_NONE;
 | 
				
			||||||
 | 
								elf_write_reloc(file->elf, reloc);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							elf_write_insn(file->elf, insn->sec,
 | 
				
			||||||
 | 
								       insn->offset, insn->len,
 | 
				
			||||||
 | 
								       sibling ? arch_ret_insn(insn->len)
 | 
				
			||||||
 | 
								               : arch_nop_insn(insn->len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->type = sibling ? INSN_RETURN : INSN_NOP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mcount && !strcmp(insn->call_dest->name, "__fentry__")) {
 | 
				
			||||||
 | 
							if (sibling)
 | 
				
			||||||
 | 
								WARN_FUNC("Tail call to __fentry__ !?!?", insn->sec, insn->offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (reloc) {
 | 
				
			||||||
 | 
								reloc->type = R_NONE;
 | 
				
			||||||
 | 
								elf_write_reloc(file->elf, reloc);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							elf_write_insn(file->elf, insn->sec,
 | 
				
			||||||
 | 
								       insn->offset, insn->len,
 | 
				
			||||||
 | 
								       arch_nop_insn(insn->len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->type = INSN_NOP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							list_add_tail(&insn->mcount_loc_node,
 | 
				
			||||||
 | 
								      &file->mcount_loc_list);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Whatever stack impact regular CALLs have, should be undone
 | 
				
			||||||
 | 
						 * by the RETURN of the called function.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Annotated intra-function calls retain the stack_ops but
 | 
				
			||||||
 | 
						 * are converted to JUMP, see read_intra_function_calls().
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						remove_insn_ops(insn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Find the destination instructions for all jumps.
 | 
					 * Find the destination instructions for all jumps.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -942,11 +1015,7 @@ static int add_jump_destinations(struct objtool_file *file)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		} else if (insn->func) {
 | 
							} else if (insn->func) {
 | 
				
			||||||
			/* internal or external sibling call (with reloc) */
 | 
								/* internal or external sibling call (with reloc) */
 | 
				
			||||||
			insn->call_dest = reloc->sym;
 | 
								add_call_dest(file, insn, reloc->sym, true);
 | 
				
			||||||
			if (insn->call_dest->static_call_tramp) {
 | 
					 | 
				
			||||||
				list_add_tail(&insn->call_node,
 | 
					 | 
				
			||||||
					      &file->static_call_list);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		} else if (reloc->sym->sec->idx) {
 | 
							} else if (reloc->sym->sec->idx) {
 | 
				
			||||||
			dest_sec = reloc->sym->sec;
 | 
								dest_sec = reloc->sym->sec;
 | 
				
			||||||
| 
						 | 
					@ -1002,13 +1071,8 @@ static int add_jump_destinations(struct objtool_file *file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
 | 
								} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
 | 
				
			||||||
				   insn->jump_dest->offset == insn->jump_dest->func->offset) {
 | 
									   insn->jump_dest->offset == insn->jump_dest->func->offset) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
				/* internal sibling call (without reloc) */
 | 
									/* internal sibling call (without reloc) */
 | 
				
			||||||
				insn->call_dest = insn->jump_dest->func;
 | 
									add_call_dest(file, insn, insn->jump_dest->func, true);
 | 
				
			||||||
				if (insn->call_dest->static_call_tramp) {
 | 
					 | 
				
			||||||
					list_add_tail(&insn->call_node,
 | 
					 | 
				
			||||||
						      &file->static_call_list);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1016,16 +1080,6 @@ static int add_jump_destinations(struct objtool_file *file)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void remove_insn_ops(struct instruction *insn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct stack_op *op, *tmp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
 | 
					 | 
				
			||||||
		list_del(&op->list);
 | 
					 | 
				
			||||||
		free(op);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
 | 
					static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct symbol *call_dest;
 | 
						struct symbol *call_dest;
 | 
				
			||||||
| 
						 | 
					@ -1044,6 +1098,7 @@ static int add_call_destinations(struct objtool_file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct instruction *insn;
 | 
						struct instruction *insn;
 | 
				
			||||||
	unsigned long dest_off;
 | 
						unsigned long dest_off;
 | 
				
			||||||
 | 
						struct symbol *dest;
 | 
				
			||||||
	struct reloc *reloc;
 | 
						struct reloc *reloc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_insn(file, insn) {
 | 
						for_each_insn(file, insn) {
 | 
				
			||||||
| 
						 | 
					@ -1053,7 +1108,9 @@ static int add_call_destinations(struct objtool_file *file)
 | 
				
			||||||
		reloc = insn_reloc(file, insn);
 | 
							reloc = insn_reloc(file, insn);
 | 
				
			||||||
		if (!reloc) {
 | 
							if (!reloc) {
 | 
				
			||||||
			dest_off = arch_jump_destination(insn);
 | 
								dest_off = arch_jump_destination(insn);
 | 
				
			||||||
			insn->call_dest = find_call_destination(insn->sec, dest_off);
 | 
								dest = find_call_destination(insn->sec, dest_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								add_call_dest(file, insn, dest, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (insn->ignore)
 | 
								if (insn->ignore)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
| 
						 | 
					@ -1071,9 +1128,8 @@ static int add_call_destinations(struct objtool_file *file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else if (reloc->sym->type == STT_SECTION) {
 | 
							} else if (reloc->sym->type == STT_SECTION) {
 | 
				
			||||||
			dest_off = arch_dest_reloc_offset(reloc->addend);
 | 
								dest_off = arch_dest_reloc_offset(reloc->addend);
 | 
				
			||||||
			insn->call_dest = find_call_destination(reloc->sym->sec,
 | 
								dest = find_call_destination(reloc->sym->sec, dest_off);
 | 
				
			||||||
								dest_off);
 | 
								if (!dest) {
 | 
				
			||||||
			if (!insn->call_dest) {
 | 
					 | 
				
			||||||
				WARN_FUNC("can't find call dest symbol at %s+0x%lx",
 | 
									WARN_FUNC("can't find call dest symbol at %s+0x%lx",
 | 
				
			||||||
					  insn->sec, insn->offset,
 | 
										  insn->sec, insn->offset,
 | 
				
			||||||
					  reloc->sym->sec->name,
 | 
										  reloc->sym->sec->name,
 | 
				
			||||||
| 
						 | 
					@ -1081,6 +1137,8 @@ static int add_call_destinations(struct objtool_file *file)
 | 
				
			||||||
				return -1;
 | 
									return -1;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								add_call_dest(file, insn, dest, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else if (arch_is_retpoline(reloc->sym)) {
 | 
							} else if (arch_is_retpoline(reloc->sym)) {
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * Retpoline calls are really dynamic calls in
 | 
								 * Retpoline calls are really dynamic calls in
 | 
				
			||||||
| 
						 | 
					@ -1096,55 +1154,7 @@ static int add_call_destinations(struct objtool_file *file)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			insn->call_dest = reloc->sym;
 | 
								add_call_dest(file, insn, reloc->sym, false);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (insn->call_dest && insn->call_dest->static_call_tramp) {
 | 
					 | 
				
			||||||
			list_add_tail(&insn->call_node,
 | 
					 | 
				
			||||||
				      &file->static_call_list);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * Many compilers cannot disable KCOV with a function attribute
 | 
					 | 
				
			||||||
		 * so they need a little help, NOP out any KCOV calls from noinstr
 | 
					 | 
				
			||||||
		 * text.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		if (insn->sec->noinstr &&
 | 
					 | 
				
			||||||
		    !strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
 | 
					 | 
				
			||||||
			if (reloc) {
 | 
					 | 
				
			||||||
				reloc->type = R_NONE;
 | 
					 | 
				
			||||||
				elf_write_reloc(file->elf, reloc);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			elf_write_insn(file->elf, insn->sec,
 | 
					 | 
				
			||||||
				       insn->offset, insn->len,
 | 
					 | 
				
			||||||
				       arch_nop_insn(insn->len));
 | 
					 | 
				
			||||||
			insn->type = INSN_NOP;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (mcount && !strcmp(insn->call_dest->name, "__fentry__")) {
 | 
					 | 
				
			||||||
			if (reloc) {
 | 
					 | 
				
			||||||
				reloc->type = R_NONE;
 | 
					 | 
				
			||||||
				elf_write_reloc(file->elf, reloc);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			elf_write_insn(file->elf, insn->sec,
 | 
					 | 
				
			||||||
				       insn->offset, insn->len,
 | 
					 | 
				
			||||||
				       arch_nop_insn(insn->len));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			insn->type = INSN_NOP;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			list_add_tail(&insn->mcount_loc_node,
 | 
					 | 
				
			||||||
				      &file->mcount_loc_list);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * Whatever stack impact regular CALLs have, should be undone
 | 
					 | 
				
			||||||
		 * by the RETURN of the called function.
 | 
					 | 
				
			||||||
		 *
 | 
					 | 
				
			||||||
		 * Annotated intra-function calls retain the stack_ops but
 | 
					 | 
				
			||||||
		 * are converted to JUMP, see read_intra_function_calls().
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		remove_insn_ops(insn);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,7 @@ unsigned long arch_jump_destination(struct instruction *insn);
 | 
				
			||||||
unsigned long arch_dest_reloc_offset(int addend);
 | 
					unsigned long arch_dest_reloc_offset(int addend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char *arch_nop_insn(int len);
 | 
					const char *arch_nop_insn(int len);
 | 
				
			||||||
 | 
					const char *arch_ret_insn(int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int arch_decode_hint_reg(u8 sp_reg, int *base);
 | 
					int arch_decode_hint_reg(u8 sp_reg, int *base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue