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

objtool: Implement stack validation 2.0

This is a major rewrite of objtool. Instead of only tracking frame
pointer changes, it now tracks all stack-related operations, including
all register saves/restores.

In addition to making stack validation more robust, this also paves the
way for undwarf generation.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/678bd94c0566c6129bcc376cddb259c4c5633004.1498659915.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Josh Poimboeuf and committed by
Ingo Molnar
baa41469 c207aee4

+1132 -322
+67 -90
tools/objtool/Documentation/stack-validation.txt
··· 127 127 128 128 c) Higher live patching compatibility rate 129 129 130 - (NOTE: This is not yet implemented) 130 + Livepatch has an optional "consistency model", which is needed for 131 + more complex patches. In order for the consistency model to work, 132 + stack traces need to be reliable (or an unreliable condition needs to 133 + be detectable). Objtool makes that possible. 131 134 132 - Currently with CONFIG_LIVEPATCH there's a basic live patching 133 - framework which is safe for roughly 85-90% of "security" fixes. But 134 - patches can't have complex features like function dependency or 135 - prototype changes, or data structure changes. 136 - 137 - There's a strong need to support patches which have the more complex 138 - features so that the patch compatibility rate for security fixes can 139 - eventually approach something resembling 100%. To achieve that, a 140 - "consistency model" is needed, which allows tasks to be safely 141 - transitioned from an unpatched state to a patched state. 142 - 143 - One of the key requirements of the currently proposed livepatch 144 - consistency model [*] is that it needs to walk the stack of each 145 - sleeping task to determine if it can be transitioned to the patched 146 - state. If objtool can ensure that stack traces are reliable, this 147 - consistency model can be used and the live patching compatibility 148 - rate can be improved significantly. 149 - 150 - [*] https://lkml.kernel.org/r/cover.1423499826.git.jpoimboe@redhat.com 151 - 135 + For more details, see the livepatch documentation in the Linux kernel 136 + source tree at Documentation/livepatch/livepatch.txt. 152 137 153 138 Rules 154 139 ----- ··· 186 201 return normally. 187 202 188 203 189 - Errors in .S files 190 - ------------------ 204 + Objtool warnings 205 + ---------------- 191 206 192 - If you're getting an error in a compiled .S file which you don't 193 - understand, first make sure that the affected code follows the above 194 - rules. 207 + For asm files, if you're getting an error which doesn't make sense, 208 + first make sure that the affected code follows the above rules. 209 + 210 + For C files, the common culprits are inline asm statements and calls to 211 + "noreturn" functions. See below for more details. 212 + 213 + Another possible cause for errors in C code is if the Makefile removes 214 + -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. 195 215 196 216 Here are some examples of common warnings reported by objtool, what 197 217 they mean, and suggestions for how to fix them. 198 218 199 219 200 - 1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup 220 + 1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup 201 221 202 222 The func() function made a function call without first saving and/or 203 - updating the frame pointer. 223 + updating the frame pointer, and CONFIG_FRAME_POINTER is enabled. 204 224 205 - If func() is indeed a callable function, add proper frame pointer 206 - logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove 207 - its ELF function annotation by changing ENDPROC to END. 225 + If the error is for an asm file, and func() is indeed a callable 226 + function, add proper frame pointer logic using the FRAME_BEGIN and 227 + FRAME_END macros. Otherwise, if it's not a callable function, remove 228 + its ELF function annotation by changing ENDPROC to END, and instead 229 + use the manual CFI hint macros in asm/undwarf.h. 208 230 209 - If you're getting this error in a .c file, see the "Errors in .c 210 - files" section. 231 + If it's a GCC-compiled .c file, the error may be because the function 232 + uses an inline asm() statement which has a "call" instruction. An 233 + asm() statement with a call instruction must declare the use of the 234 + stack pointer in its output operand. For example, on x86_64: 211 235 236 + register void *__sp asm("rsp"); 237 + asm volatile("call func" : "+r" (__sp)); 212 238 213 - 2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function 214 - 215 - A return instruction was detected, but objtool couldn't find a way 216 - for a callable function to reach the instruction. 217 - 218 - If the return instruction is inside (or reachable from) a callable 219 - function, the function needs to be annotated with the ENTRY/ENDPROC 220 - macros. 221 - 222 - If you _really_ need a return instruction outside of a function, and 223 - are 100% sure that it won't affect stack traces, you can tell 224 - objtool to ignore it. See the "Adding exceptions" section below. 239 + Otherwise the stack frame may not get created before the call. 225 240 226 241 227 - 3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction 242 + 2. file.o: warning: objtool: .text+0x53: unreachable instruction 228 243 229 - The instruction lives inside of a callable function, but there's no 230 - possible control flow path from the beginning of the function to the 231 - instruction. 244 + Objtool couldn't find a code path to reach the instruction. 232 245 233 - If the instruction is actually needed, and it's actually in a 234 - callable function, ensure that its function is properly annotated 235 - with ENTRY/ENDPROC. 246 + If the error is for an asm file, and the instruction is inside (or 247 + reachable from) a callable function, the function should be annotated 248 + with the ENTRY/ENDPROC macros (ENDPROC is the important one). 249 + Otherwise, the code should probably be annotated with the CFI hint 250 + macros in asm/undwarf.h so objtool and the unwinder can know the 251 + stack state associated with the code. 252 + 253 + If you're 100% sure the code won't affect stack traces, or if you're 254 + a just a bad person, you can tell objtool to ignore it. See the 255 + "Adding exceptions" section below. 236 256 237 257 If it's not actually in a callable function (e.g. kernel entry code), 238 258 change ENDPROC to END. 239 259 240 260 241 - 4. asm_file.o: warning: objtool: func(): can't find starting instruction 261 + 4. file.o: warning: objtool: func(): can't find starting instruction 242 262 or 243 - asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction 263 + file.o: warning: objtool: func()+0x11dd: can't decode instruction 244 264 245 - Did you put data in a text section? If so, that can confuse 265 + Does the file have data in a text section? If so, that can confuse 246 266 objtool's instruction decoder. Move the data to a more appropriate 247 267 section like .data or .rodata. 248 268 249 269 250 - 5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction 270 + 5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function 251 271 252 - This is a kernel entry/exit instruction like sysenter or sysret. 253 - Such instructions aren't allowed in a callable function, and are most 254 - likely part of the kernel entry code. 272 + This is a kernel entry/exit instruction like sysenter or iret. Such 273 + instructions aren't allowed in a callable function, and are most 274 + likely part of the kernel entry code. They should usually not have 275 + the callable function annotation (ENDPROC) and should always be 276 + annotated with the CFI hint macros in asm/undwarf.h. 255 277 256 - If the instruction isn't actually in a callable function, change 257 - ENDPROC to END. 258 278 279 + 6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame 259 280 260 - 6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer 261 - 262 - This is a dynamic jump or a jump to an undefined symbol. Stacktool 281 + This is a dynamic jump or a jump to an undefined symbol. Objtool 263 282 assumed it's a sibling call and detected that the frame pointer 264 283 wasn't first restored to its original state. 265 284 ··· 271 282 destination code to the local file. 272 283 273 284 If the instruction is not actually in a callable function (e.g. 274 - kernel entry code), change ENDPROC to END. 285 + kernel entry code), change ENDPROC to END and annotate manually with 286 + the CFI hint macros in asm/undwarf.h. 275 287 276 288 277 - 7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch 289 + 7. file: warning: objtool: func()+0x5c: stack state mismatch 278 290 279 291 The instruction's frame pointer state is inconsistent, depending on 280 292 which execution path was taken to reach the instruction. 281 293 282 - Make sure the function pushes and sets up the frame pointer (for 283 - x86_64, this means rbp) at the beginning of the function and pops it 284 - at the end of the function. Also make sure that no other code in the 285 - function touches the frame pointer. 294 + Make sure that, when CONFIG_FRAME_POINTER is enabled, the function 295 + pushes and sets up the frame pointer (for x86_64, this means rbp) at 296 + the beginning of the function and pops it at the end of the function. 297 + Also make sure that no other code in the function touches the frame 298 + pointer. 299 + 300 + Another possibility is that the code has some asm or inline asm which 301 + does some unusual things to the stack or the frame pointer. In such 302 + cases it's probably appropriate to use the CFI hint macros in 303 + asm/undwarf.h. 286 304 287 305 288 - Errors in .c files 289 - ------------------ 290 - 291 - 1. c_file.o: warning: objtool: funcA() falls through to next function funcB() 306 + 8. file.o: warning: objtool: funcA() falls through to next function funcB() 292 307 293 308 This means that funcA() doesn't end with a return instruction or an 294 309 unconditional jump, and that objtool has determined that the function ··· 311 318 might be corrupt due to a gcc bug. For more details, see: 312 319 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 313 320 314 - 2. If you're getting any other objtool error in a compiled .c file, it 315 - may be because the file uses an asm() statement which has a "call" 316 - instruction. An asm() statement with a call instruction must declare 317 - the use of the stack pointer in its output operand. For example, on 318 - x86_64: 319 - 320 - register void *__sp asm("rsp"); 321 - asm volatile("call func" : "+r" (__sp)); 322 - 323 - Otherwise the stack frame may not get created before the call. 324 - 325 - 3. Another possible cause for errors in C code is if the Makefile removes 326 - -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. 327 - 328 - Also see the above section for .S file errors for more information what 329 - the individual error messages mean. 330 321 331 322 If the error doesn't seem to make sense, it could be a bug in objtool. 332 323 Feel free to ask the objtool maintainer for help.
+1 -1
tools/objtool/Makefile
··· 25 25 all: $(OBJTOOL) 26 26 27 27 INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi 28 - CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES) 28 + CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES) 29 29 LDFLAGS += -lelf $(LIBSUBCMD) 30 30 31 31 # Allow old libelf to be used:
+51 -13
tools/objtool/arch.h
··· 19 19 #define _ARCH_H 20 20 21 21 #include <stdbool.h> 22 + #include <linux/list.h> 22 23 #include "elf.h" 24 + #include "cfi.h" 23 25 24 - #define INSN_FP_SAVE 1 25 - #define INSN_FP_SETUP 2 26 - #define INSN_FP_RESTORE 3 27 - #define INSN_JUMP_CONDITIONAL 4 28 - #define INSN_JUMP_UNCONDITIONAL 5 29 - #define INSN_JUMP_DYNAMIC 6 30 - #define INSN_CALL 7 31 - #define INSN_CALL_DYNAMIC 8 32 - #define INSN_RETURN 9 33 - #define INSN_CONTEXT_SWITCH 10 34 - #define INSN_NOP 11 35 - #define INSN_OTHER 12 26 + #define INSN_JUMP_CONDITIONAL 1 27 + #define INSN_JUMP_UNCONDITIONAL 2 28 + #define INSN_JUMP_DYNAMIC 3 29 + #define INSN_CALL 4 30 + #define INSN_CALL_DYNAMIC 5 31 + #define INSN_RETURN 6 32 + #define INSN_CONTEXT_SWITCH 7 33 + #define INSN_STACK 8 34 + #define INSN_NOP 9 35 + #define INSN_OTHER 10 36 36 #define INSN_LAST INSN_OTHER 37 + 38 + enum op_dest_type { 39 + OP_DEST_REG, 40 + OP_DEST_REG_INDIRECT, 41 + OP_DEST_MEM, 42 + OP_DEST_PUSH, 43 + OP_DEST_LEAVE, 44 + }; 45 + 46 + struct op_dest { 47 + enum op_dest_type type; 48 + unsigned char reg; 49 + int offset; 50 + }; 51 + 52 + enum op_src_type { 53 + OP_SRC_REG, 54 + OP_SRC_REG_INDIRECT, 55 + OP_SRC_CONST, 56 + OP_SRC_POP, 57 + OP_SRC_ADD, 58 + OP_SRC_AND, 59 + }; 60 + 61 + struct op_src { 62 + enum op_src_type type; 63 + unsigned char reg; 64 + int offset; 65 + }; 66 + 67 + struct stack_op { 68 + struct op_dest dest; 69 + struct op_src src; 70 + }; 71 + 72 + void arch_initial_func_cfi_state(struct cfi_state *state); 37 73 38 74 int arch_decode_instruction(struct elf *elf, struct section *sec, 39 75 unsigned long offset, unsigned int maxlen, 40 76 unsigned int *len, unsigned char *type, 41 - unsigned long *displacement); 77 + unsigned long *immediate, struct stack_op *op); 78 + 79 + bool arch_callee_saved_reg(unsigned char reg); 42 80 43 81 #endif /* _ARCH_H */
+362 -38
tools/objtool/arch/x86/decode.c
··· 27 27 #include "../../arch.h" 28 28 #include "../../warn.h" 29 29 30 + static unsigned char op_to_cfi_reg[][2] = { 31 + {CFI_AX, CFI_R8}, 32 + {CFI_CX, CFI_R9}, 33 + {CFI_DX, CFI_R10}, 34 + {CFI_BX, CFI_R11}, 35 + {CFI_SP, CFI_R12}, 36 + {CFI_BP, CFI_R13}, 37 + {CFI_SI, CFI_R14}, 38 + {CFI_DI, CFI_R15}, 39 + }; 40 + 30 41 static int is_x86_64(struct elf *elf) 31 42 { 32 43 switch (elf->ehdr.e_machine) { ··· 51 40 } 52 41 } 53 42 43 + bool arch_callee_saved_reg(unsigned char reg) 44 + { 45 + switch (reg) { 46 + case CFI_BP: 47 + case CFI_BX: 48 + case CFI_R12: 49 + case CFI_R13: 50 + case CFI_R14: 51 + case CFI_R15: 52 + return true; 53 + 54 + case CFI_AX: 55 + case CFI_CX: 56 + case CFI_DX: 57 + case CFI_SI: 58 + case CFI_DI: 59 + case CFI_SP: 60 + case CFI_R8: 61 + case CFI_R9: 62 + case CFI_R10: 63 + case CFI_R11: 64 + case CFI_RA: 65 + default: 66 + return false; 67 + } 68 + } 69 + 54 70 int arch_decode_instruction(struct elf *elf, struct section *sec, 55 71 unsigned long offset, unsigned int maxlen, 56 72 unsigned int *len, unsigned char *type, 57 - unsigned long *immediate) 73 + unsigned long *immediate, struct stack_op *op) 58 74 { 59 75 struct insn insn; 60 - int x86_64; 61 - unsigned char op1, op2, ext; 76 + int x86_64, sign; 77 + unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, 78 + modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0, 79 + sib = 0; 62 80 63 81 x86_64 = is_x86_64(elf); 64 82 if (x86_64 == -1) 65 83 return -1; 66 84 67 - insn_init(&insn, (void *)(sec->data + offset), maxlen, x86_64); 85 + insn_init(&insn, sec->data->d_buf + offset, maxlen, x86_64); 68 86 insn_get_length(&insn); 69 - insn_get_opcode(&insn); 70 - insn_get_modrm(&insn); 71 - insn_get_immediate(&insn); 72 87 73 88 if (!insn_complete(&insn)) { 74 89 WARN_FUNC("can't decode instruction", sec, offset); ··· 110 73 op1 = insn.opcode.bytes[0]; 111 74 op2 = insn.opcode.bytes[1]; 112 75 76 + if (insn.rex_prefix.nbytes) { 77 + rex = insn.rex_prefix.bytes[0]; 78 + rex_w = X86_REX_W(rex) >> 3; 79 + rex_r = X86_REX_R(rex) >> 2; 80 + rex_b = X86_REX_B(rex); 81 + } 82 + 83 + if (insn.modrm.nbytes) { 84 + modrm = insn.modrm.bytes[0]; 85 + modrm_mod = X86_MODRM_MOD(modrm); 86 + modrm_reg = X86_MODRM_REG(modrm); 87 + modrm_rm = X86_MODRM_RM(modrm); 88 + } 89 + 90 + if (insn.sib.nbytes) 91 + sib = insn.sib.bytes[0]; 92 + 113 93 switch (op1) { 114 - case 0x55: 115 - if (!insn.rex_prefix.nbytes) 116 - /* push rbp */ 117 - *type = INSN_FP_SAVE; 94 + 95 + case 0x1: 96 + case 0x29: 97 + if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) { 98 + 99 + /* add/sub reg, %rsp */ 100 + *type = INSN_STACK; 101 + op->src.type = OP_SRC_ADD; 102 + op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 103 + op->dest.type = OP_SRC_REG; 104 + op->dest.reg = CFI_SP; 105 + } 118 106 break; 119 107 120 - case 0x5d: 121 - if (!insn.rex_prefix.nbytes) 122 - /* pop rbp */ 123 - *type = INSN_FP_RESTORE; 108 + case 0x50 ... 0x57: 109 + 110 + /* push reg */ 111 + *type = INSN_STACK; 112 + op->src.type = OP_SRC_REG; 113 + op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b]; 114 + op->dest.type = OP_DEST_PUSH; 115 + 116 + break; 117 + 118 + case 0x58 ... 0x5f: 119 + 120 + /* pop reg */ 121 + *type = INSN_STACK; 122 + op->src.type = OP_SRC_POP; 123 + op->dest.type = OP_DEST_REG; 124 + op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b]; 125 + 126 + break; 127 + 128 + case 0x68: 129 + case 0x6a: 130 + /* push immediate */ 131 + *type = INSN_STACK; 132 + op->src.type = OP_SRC_CONST; 133 + op->dest.type = OP_DEST_PUSH; 124 134 break; 125 135 126 136 case 0x70 ... 0x7f: 127 137 *type = INSN_JUMP_CONDITIONAL; 128 138 break; 129 139 140 + case 0x81: 141 + case 0x83: 142 + if (rex != 0x48) 143 + break; 144 + 145 + if (modrm == 0xe4) { 146 + /* and imm, %rsp */ 147 + *type = INSN_STACK; 148 + op->src.type = OP_SRC_AND; 149 + op->src.reg = CFI_SP; 150 + op->src.offset = insn.immediate.value; 151 + op->dest.type = OP_DEST_REG; 152 + op->dest.reg = CFI_SP; 153 + break; 154 + } 155 + 156 + if (modrm == 0xc4) 157 + sign = 1; 158 + else if (modrm == 0xec) 159 + sign = -1; 160 + else 161 + break; 162 + 163 + /* add/sub imm, %rsp */ 164 + *type = INSN_STACK; 165 + op->src.type = OP_SRC_ADD; 166 + op->src.reg = CFI_SP; 167 + op->src.offset = insn.immediate.value * sign; 168 + op->dest.type = OP_DEST_REG; 169 + op->dest.reg = CFI_SP; 170 + break; 171 + 130 172 case 0x89: 131 - if (insn.rex_prefix.nbytes == 1 && 132 - insn.rex_prefix.bytes[0] == 0x48 && 133 - insn.modrm.nbytes && insn.modrm.bytes[0] == 0xe5) 134 - /* mov rsp, rbp */ 135 - *type = INSN_FP_SETUP; 173 + if (rex == 0x48 && modrm == 0xe5) { 174 + 175 + /* mov %rsp, %rbp */ 176 + *type = INSN_STACK; 177 + op->src.type = OP_SRC_REG; 178 + op->src.reg = CFI_SP; 179 + op->dest.type = OP_DEST_REG; 180 + op->dest.reg = CFI_BP; 181 + break; 182 + } 183 + /* fallthrough */ 184 + case 0x88: 185 + if (!rex_b && 186 + (modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) { 187 + 188 + /* mov reg, disp(%rbp) */ 189 + *type = INSN_STACK; 190 + op->src.type = OP_SRC_REG; 191 + op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 192 + op->dest.type = OP_DEST_REG_INDIRECT; 193 + op->dest.reg = CFI_BP; 194 + op->dest.offset = insn.displacement.value; 195 + 196 + } else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) { 197 + 198 + /* mov reg, disp(%rsp) */ 199 + *type = INSN_STACK; 200 + op->src.type = OP_SRC_REG; 201 + op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 202 + op->dest.type = OP_DEST_REG_INDIRECT; 203 + op->dest.reg = CFI_SP; 204 + op->dest.offset = insn.displacement.value; 205 + } 206 + 207 + break; 208 + 209 + case 0x8b: 210 + if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) { 211 + 212 + /* mov disp(%rbp), reg */ 213 + *type = INSN_STACK; 214 + op->src.type = OP_SRC_REG_INDIRECT; 215 + op->src.reg = CFI_BP; 216 + op->src.offset = insn.displacement.value; 217 + op->dest.type = OP_DEST_REG; 218 + op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r]; 219 + 220 + } else if (rex_w && !rex_b && sib == 0x24 && 221 + modrm_mod != 3 && modrm_rm == 4) { 222 + 223 + /* mov disp(%rsp), reg */ 224 + *type = INSN_STACK; 225 + op->src.type = OP_SRC_REG_INDIRECT; 226 + op->src.reg = CFI_SP; 227 + op->src.offset = insn.displacement.value; 228 + op->dest.type = OP_DEST_REG; 229 + op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r]; 230 + } 231 + 136 232 break; 137 233 138 234 case 0x8d: 139 - if (insn.rex_prefix.nbytes && 140 - insn.rex_prefix.bytes[0] == 0x48 && 141 - insn.modrm.nbytes && insn.modrm.bytes[0] == 0x2c && 142 - insn.sib.nbytes && insn.sib.bytes[0] == 0x24) 143 - /* lea %(rsp), %rbp */ 144 - *type = INSN_FP_SETUP; 235 + if (rex == 0x48 && modrm == 0x65) { 236 + 237 + /* lea -disp(%rbp), %rsp */ 238 + *type = INSN_STACK; 239 + op->src.type = OP_SRC_ADD; 240 + op->src.reg = CFI_BP; 241 + op->src.offset = insn.displacement.value; 242 + op->dest.type = OP_DEST_REG; 243 + op->dest.reg = CFI_SP; 244 + break; 245 + } 246 + 247 + if (rex == 0x4c && modrm == 0x54 && sib == 0x24 && 248 + insn.displacement.value == 8) { 249 + 250 + /* 251 + * lea 0x8(%rsp), %r10 252 + * 253 + * Here r10 is the "drap" pointer, used as a stack 254 + * pointer helper when the stack gets realigned. 255 + */ 256 + *type = INSN_STACK; 257 + op->src.type = OP_SRC_ADD; 258 + op->src.reg = CFI_SP; 259 + op->src.offset = 8; 260 + op->dest.type = OP_DEST_REG; 261 + op->dest.reg = CFI_R10; 262 + break; 263 + } 264 + 265 + if (rex == 0x4c && modrm == 0x6c && sib == 0x24 && 266 + insn.displacement.value == 16) { 267 + 268 + /* 269 + * lea 0x10(%rsp), %r13 270 + * 271 + * Here r13 is the "drap" pointer, used as a stack 272 + * pointer helper when the stack gets realigned. 273 + */ 274 + *type = INSN_STACK; 275 + op->src.type = OP_SRC_ADD; 276 + op->src.reg = CFI_SP; 277 + op->src.offset = 16; 278 + op->dest.type = OP_DEST_REG; 279 + op->dest.reg = CFI_R13; 280 + break; 281 + } 282 + 283 + if (rex == 0x49 && modrm == 0x62 && 284 + insn.displacement.value == -8) { 285 + 286 + /* 287 + * lea -0x8(%r10), %rsp 288 + * 289 + * Restoring rsp back to its original value after a 290 + * stack realignment. 291 + */ 292 + *type = INSN_STACK; 293 + op->src.type = OP_SRC_ADD; 294 + op->src.reg = CFI_R10; 295 + op->src.offset = -8; 296 + op->dest.type = OP_DEST_REG; 297 + op->dest.reg = CFI_SP; 298 + break; 299 + } 300 + 301 + if (rex == 0x49 && modrm == 0x65 && 302 + insn.displacement.value == -16) { 303 + 304 + /* 305 + * lea -0x10(%r13), %rsp 306 + * 307 + * Restoring rsp back to its original value after a 308 + * stack realignment. 309 + */ 310 + *type = INSN_STACK; 311 + op->src.type = OP_SRC_ADD; 312 + op->src.reg = CFI_R13; 313 + op->src.offset = -16; 314 + op->dest.type = OP_DEST_REG; 315 + op->dest.reg = CFI_SP; 316 + break; 317 + } 318 + 319 + break; 320 + 321 + case 0x8f: 322 + /* pop to mem */ 323 + *type = INSN_STACK; 324 + op->src.type = OP_SRC_POP; 325 + op->dest.type = OP_DEST_MEM; 145 326 break; 146 327 147 328 case 0x90: 148 329 *type = INSN_NOP; 149 330 break; 150 331 332 + case 0x9c: 333 + /* pushf */ 334 + *type = INSN_STACK; 335 + op->src.type = OP_SRC_CONST; 336 + op->dest.type = OP_DEST_PUSH; 337 + break; 338 + 339 + case 0x9d: 340 + /* popf */ 341 + *type = INSN_STACK; 342 + op->src.type = OP_SRC_POP; 343 + op->dest.type = OP_DEST_MEM; 344 + break; 345 + 151 346 case 0x0f: 347 + 152 348 if (op2 >= 0x80 && op2 <= 0x8f) 153 349 *type = INSN_JUMP_CONDITIONAL; 154 350 else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 || 155 351 op2 == 0x35) 352 + 156 353 /* sysenter, sysret */ 157 354 *type = INSN_CONTEXT_SWITCH; 355 + 158 356 else if (op2 == 0x0d || op2 == 0x1f) 357 + 159 358 /* nopl/nopw */ 160 359 *type = INSN_NOP; 161 - else if (op2 == 0x01 && insn.modrm.nbytes && 162 - (insn.modrm.bytes[0] == 0xc2 || 163 - insn.modrm.bytes[0] == 0xd8)) 164 - /* vmlaunch, vmrun */ 165 - *type = INSN_CONTEXT_SWITCH; 360 + 361 + else if (op2 == 0xa0 || op2 == 0xa8) { 362 + 363 + /* push fs/gs */ 364 + *type = INSN_STACK; 365 + op->src.type = OP_SRC_CONST; 366 + op->dest.type = OP_DEST_PUSH; 367 + 368 + } else if (op2 == 0xa1 || op2 == 0xa9) { 369 + 370 + /* pop fs/gs */ 371 + *type = INSN_STACK; 372 + op->src.type = OP_SRC_POP; 373 + op->dest.type = OP_DEST_MEM; 374 + } 166 375 167 376 break; 168 377 169 - case 0xc9: /* leave */ 170 - *type = INSN_FP_RESTORE; 378 + case 0xc9: 379 + /* 380 + * leave 381 + * 382 + * equivalent to: 383 + * mov bp, sp 384 + * pop bp 385 + */ 386 + *type = INSN_STACK; 387 + op->dest.type = OP_DEST_LEAVE; 388 + 171 389 break; 172 390 173 - case 0xe3: /* jecxz/jrcxz */ 391 + case 0xe3: 392 + /* jecxz/jrcxz */ 174 393 *type = INSN_JUMP_CONDITIONAL; 175 394 break; 176 395 ··· 451 158 break; 452 159 453 160 case 0xff: 454 - ext = X86_MODRM_REG(insn.modrm.bytes[0]); 455 - if (ext == 2 || ext == 3) 161 + if (modrm_reg == 2 || modrm_reg == 3) 162 + 456 163 *type = INSN_CALL_DYNAMIC; 457 - else if (ext == 4) 164 + 165 + else if (modrm_reg == 4) 166 + 458 167 *type = INSN_JUMP_DYNAMIC; 459 - else if (ext == 5) /*jmpf */ 168 + 169 + else if (modrm_reg == 5) 170 + 171 + /* jmpf */ 460 172 *type = INSN_CONTEXT_SWITCH; 173 + 174 + else if (modrm_reg == 6) { 175 + 176 + /* push from mem */ 177 + *type = INSN_STACK; 178 + op->src.type = OP_SRC_CONST; 179 + op->dest.type = OP_DEST_PUSH; 180 + } 461 181 462 182 break; 463 183 ··· 481 175 *immediate = insn.immediate.nbytes ? insn.immediate.value : 0; 482 176 483 177 return 0; 178 + } 179 + 180 + void arch_initial_func_cfi_state(struct cfi_state *state) 181 + { 182 + int i; 183 + 184 + for (i = 0; i < CFI_NUM_REGS; i++) { 185 + state->regs[i].base = CFI_UNDEFINED; 186 + state->regs[i].offset = 0; 187 + } 188 + 189 + /* initial CFA (call frame address) */ 190 + state->cfa.base = CFI_SP; 191 + state->cfa.offset = 8; 192 + 193 + /* initial RA (return address) */ 194 + state->regs[16].base = CFI_CFA; 195 + state->regs[16].offset = -8; 484 196 }
+55
tools/objtool/cfi.h
··· 1 + /* 2 + * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public License 6 + * as published by the Free Software Foundation; either version 2 7 + * of the License, or (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #ifndef _OBJTOOL_CFI_H 19 + #define _OBJTOOL_CFI_H 20 + 21 + #define CFI_UNDEFINED -1 22 + #define CFI_CFA -2 23 + #define CFI_SP_INDIRECT -3 24 + #define CFI_BP_INDIRECT -4 25 + 26 + #define CFI_AX 0 27 + #define CFI_DX 1 28 + #define CFI_CX 2 29 + #define CFI_BX 3 30 + #define CFI_SI 4 31 + #define CFI_DI 5 32 + #define CFI_BP 6 33 + #define CFI_SP 7 34 + #define CFI_R8 8 35 + #define CFI_R9 9 36 + #define CFI_R10 10 37 + #define CFI_R11 11 38 + #define CFI_R12 12 39 + #define CFI_R13 13 40 + #define CFI_R14 14 41 + #define CFI_R15 15 42 + #define CFI_RA 16 43 + #define CFI_NUM_REGS 17 44 + 45 + struct cfi_reg { 46 + int base; 47 + int offset; 48 + }; 49 + 50 + struct cfi_state { 51 + struct cfi_reg cfa; 52 + struct cfi_reg regs[CFI_NUM_REGS]; 53 + }; 54 + 55 + #endif /* _OBJTOOL_CFI_H */
+535 -141
tools/objtool/check.c
··· 27 27 #include <linux/hashtable.h> 28 28 #include <linux/kernel.h> 29 29 30 - #define STATE_FP_SAVED 0x1 31 - #define STATE_FP_SETUP 0x2 32 - #define STATE_FENTRY 0x4 33 - 34 30 struct alternative { 35 31 struct list_head list; 36 32 struct instruction *insn; ··· 34 38 35 39 const char *objname; 36 40 static bool nofp; 41 + struct cfi_state initial_func_cfi; 37 42 38 43 static struct instruction *find_insn(struct objtool_file *file, 39 44 struct section *sec, unsigned long offset) ··· 53 56 { 54 57 struct instruction *next = list_next_entry(insn, list); 55 58 56 - if (&next->list == &file->insn_list || next->sec != insn->sec) 59 + if (!next || &next->list == &file->insn_list || next->sec != insn->sec) 57 60 return NULL; 58 61 59 62 return next; ··· 64 67 struct section *sec; 65 68 struct symbol *sym; 66 69 67 - list_for_each_entry(sec, &file->elf->sections, list) 70 + for_each_sec(file, sec) 68 71 list_for_each_entry(sym, &sec->symbol_list, list) 69 72 if (!strncmp(sym->name, "__gcov_.", 8)) 70 73 return true; 71 74 72 75 return false; 73 76 } 74 - 75 - #define for_each_insn(file, insn) \ 76 - list_for_each_entry(insn, &file->insn_list, list) 77 77 78 78 #define func_for_each_insn(file, func, insn) \ 79 79 for (insn = find_insn(file, func->sec, func->offset); \ ··· 88 94 #define sec_for_each_insn_from(file, insn) \ 89 95 for (; insn; insn = next_insn_same_sec(file, insn)) 90 96 97 + #define sec_for_each_insn_continue(file, insn) \ 98 + for (insn = next_insn_same_sec(file, insn); insn; \ 99 + insn = next_insn_same_sec(file, insn)) 91 100 92 101 /* 93 102 * Check if the function has been manually whitelisted with the ··· 100 103 static bool ignore_func(struct objtool_file *file, struct symbol *func) 101 104 { 102 105 struct rela *rela; 103 - struct instruction *insn; 104 106 105 107 /* check for STACK_FRAME_NON_STANDARD */ 106 108 if (file->whitelist && file->whitelist->rela) ··· 111 115 if (rela->sym->type == STT_FUNC && rela->sym == func) 112 116 return true; 113 117 } 114 - 115 - /* check if it has a context switching instruction */ 116 - func_for_each_insn(file, func, insn) 117 - if (insn->type == INSN_CONTEXT_SWITCH) 118 - return true; 119 118 120 119 return false; 121 120 } ··· 225 234 return __dead_end_function(file, func, 0); 226 235 } 227 236 237 + static void clear_insn_state(struct insn_state *state) 238 + { 239 + int i; 240 + 241 + memset(state, 0, sizeof(*state)); 242 + state->cfa.base = CFI_UNDEFINED; 243 + for (i = 0; i < CFI_NUM_REGS; i++) 244 + state->regs[i].base = CFI_UNDEFINED; 245 + state->drap_reg = CFI_UNDEFINED; 246 + } 247 + 228 248 /* 229 249 * Call the arch-specific instruction decoder for all the instructions and add 230 250 * them to the global instruction list. ··· 248 246 struct instruction *insn; 249 247 int ret; 250 248 251 - list_for_each_entry(sec, &file->elf->sections, list) { 249 + for_each_sec(file, sec) { 252 250 253 251 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 254 252 continue; 255 253 256 254 for (offset = 0; offset < sec->len; offset += insn->len) { 257 255 insn = malloc(sizeof(*insn)); 256 + if (!insn) { 257 + WARN("malloc failed"); 258 + return -1; 259 + } 258 260 memset(insn, 0, sizeof(*insn)); 259 - 260 261 INIT_LIST_HEAD(&insn->alts); 262 + clear_insn_state(&insn->state); 263 + 261 264 insn->sec = sec; 262 265 insn->offset = offset; 263 266 264 267 ret = arch_decode_instruction(file->elf, sec, offset, 265 268 sec->len - offset, 266 269 &insn->len, &insn->type, 267 - &insn->immediate); 270 + &insn->immediate, 271 + &insn->stack_op); 268 272 if (ret) 269 273 return ret; 270 274 ··· 360 352 struct section *sec; 361 353 struct symbol *func; 362 354 363 - list_for_each_entry(sec, &file->elf->sections, list) { 355 + for_each_sec(file, sec) { 364 356 list_for_each_entry(func, &sec->symbol_list, list) { 365 357 if (func->type != STT_FUNC) 366 358 continue; ··· 369 361 continue; 370 362 371 363 func_for_each_insn(file, func, insn) 372 - insn->visited = true; 364 + insn->ignore = true; 373 365 } 374 366 } 375 367 } ··· 389 381 insn->type != INSN_JUMP_UNCONDITIONAL) 390 382 continue; 391 383 392 - /* skip ignores */ 393 - if (insn->visited) 384 + if (insn->ignore) 394 385 continue; 395 386 396 387 rela = find_rela_by_dest_range(insn->sec, insn->offset, ··· 526 519 } 527 520 memset(fake_jump, 0, sizeof(*fake_jump)); 528 521 INIT_LIST_HEAD(&fake_jump->alts); 522 + clear_insn_state(&fake_jump->state); 523 + 529 524 fake_jump->sec = special_alt->new_sec; 530 525 fake_jump->offset = -1; 531 526 fake_jump->type = INSN_JUMP_UNCONDITIONAL; 532 527 fake_jump->jump_dest = list_next_entry(last_orig_insn, list); 528 + fake_jump->ignore = true; 533 529 534 530 if (!special_alt->new_len) { 535 531 *new_insn = fake_jump; ··· 854 844 if (!file->rodata || !file->rodata->rela) 855 845 return 0; 856 846 857 - list_for_each_entry(sec, &file->elf->sections, list) { 847 + for_each_sec(file, sec) { 858 848 list_for_each_entry(func, &sec->symbol_list, list) { 859 849 if (func->type != STT_FUNC) 860 850 continue; ··· 911 901 return false; 912 902 } 913 903 914 - static bool has_modified_stack_frame(struct instruction *insn) 904 + static bool has_modified_stack_frame(struct insn_state *state) 915 905 { 916 - return (insn->state & STATE_FP_SAVED) || 917 - (insn->state & STATE_FP_SETUP); 906 + int i; 907 + 908 + if (state->cfa.base != initial_func_cfi.cfa.base || 909 + state->cfa.offset != initial_func_cfi.cfa.offset || 910 + state->stack_size != initial_func_cfi.cfa.offset || 911 + state->drap) 912 + return true; 913 + 914 + for (i = 0; i < CFI_NUM_REGS; i++) 915 + if (state->regs[i].base != initial_func_cfi.regs[i].base || 916 + state->regs[i].offset != initial_func_cfi.regs[i].offset) 917 + return true; 918 + 919 + return false; 918 920 } 919 921 920 - static bool has_valid_stack_frame(struct instruction *insn) 922 + static bool has_valid_stack_frame(struct insn_state *state) 921 923 { 922 - return (insn->state & STATE_FP_SAVED) && 923 - (insn->state & STATE_FP_SETUP); 924 + if (state->cfa.base == CFI_BP && state->regs[CFI_BP].base == CFI_CFA && 925 + state->regs[CFI_BP].offset == -16) 926 + return true; 927 + 928 + if (state->drap && state->regs[CFI_BP].base == CFI_BP) 929 + return true; 930 + 931 + return false; 924 932 } 925 933 926 - static unsigned int frame_state(unsigned long state) 934 + static void save_reg(struct insn_state *state, unsigned char reg, int base, 935 + int offset) 927 936 { 928 - return (state & (STATE_FP_SAVED | STATE_FP_SETUP)); 937 + if ((arch_callee_saved_reg(reg) || 938 + (state->drap && reg == state->drap_reg)) && 939 + state->regs[reg].base == CFI_UNDEFINED) { 940 + state->regs[reg].base = base; 941 + state->regs[reg].offset = offset; 942 + } 943 + } 944 + 945 + static void restore_reg(struct insn_state *state, unsigned char reg) 946 + { 947 + state->regs[reg].base = CFI_UNDEFINED; 948 + state->regs[reg].offset = 0; 949 + } 950 + 951 + /* 952 + * A note about DRAP stack alignment: 953 + * 954 + * GCC has the concept of a DRAP register, which is used to help keep track of 955 + * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP 956 + * register. The typical DRAP pattern is: 957 + * 958 + * 4c 8d 54 24 08 lea 0x8(%rsp),%r10 959 + * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp 960 + * 41 ff 72 f8 pushq -0x8(%r10) 961 + * 55 push %rbp 962 + * 48 89 e5 mov %rsp,%rbp 963 + * (more pushes) 964 + * 41 52 push %r10 965 + * ... 966 + * 41 5a pop %r10 967 + * (more pops) 968 + * 5d pop %rbp 969 + * 49 8d 62 f8 lea -0x8(%r10),%rsp 970 + * c3 retq 971 + * 972 + * There are some variations in the epilogues, like: 973 + * 974 + * 5b pop %rbx 975 + * 41 5a pop %r10 976 + * 41 5c pop %r12 977 + * 41 5d pop %r13 978 + * 41 5e pop %r14 979 + * c9 leaveq 980 + * 49 8d 62 f8 lea -0x8(%r10),%rsp 981 + * c3 retq 982 + * 983 + * and: 984 + * 985 + * 4c 8b 55 e8 mov -0x18(%rbp),%r10 986 + * 48 8b 5d e0 mov -0x20(%rbp),%rbx 987 + * 4c 8b 65 f0 mov -0x10(%rbp),%r12 988 + * 4c 8b 6d f8 mov -0x8(%rbp),%r13 989 + * c9 leaveq 990 + * 49 8d 62 f8 lea -0x8(%r10),%rsp 991 + * c3 retq 992 + * 993 + * Sometimes r13 is used as the DRAP register, in which case it's saved and 994 + * restored beforehand: 995 + * 996 + * 41 55 push %r13 997 + * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13 998 + * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 999 + * ... 1000 + * 49 8d 65 f0 lea -0x10(%r13),%rsp 1001 + * 41 5d pop %r13 1002 + * c3 retq 1003 + */ 1004 + static int update_insn_state(struct instruction *insn, struct insn_state *state) 1005 + { 1006 + struct stack_op *op = &insn->stack_op; 1007 + struct cfi_reg *cfa = &state->cfa; 1008 + struct cfi_reg *regs = state->regs; 1009 + 1010 + /* stack operations don't make sense with an undefined CFA */ 1011 + if (cfa->base == CFI_UNDEFINED) { 1012 + if (insn->func) { 1013 + WARN_FUNC("undefined stack state", insn->sec, insn->offset); 1014 + return -1; 1015 + } 1016 + return 0; 1017 + } 1018 + 1019 + switch (op->dest.type) { 1020 + 1021 + case OP_DEST_REG: 1022 + switch (op->src.type) { 1023 + 1024 + case OP_SRC_REG: 1025 + if (cfa->base == op->src.reg && cfa->base == CFI_SP && 1026 + op->dest.reg == CFI_BP && regs[CFI_BP].base == CFI_CFA && 1027 + regs[CFI_BP].offset == -cfa->offset) { 1028 + 1029 + /* mov %rsp, %rbp */ 1030 + cfa->base = op->dest.reg; 1031 + state->bp_scratch = false; 1032 + } else if (state->drap) { 1033 + 1034 + /* drap: mov %rsp, %rbp */ 1035 + regs[CFI_BP].base = CFI_BP; 1036 + regs[CFI_BP].offset = -state->stack_size; 1037 + state->bp_scratch = false; 1038 + } else if (!nofp) { 1039 + 1040 + WARN_FUNC("unknown stack-related register move", 1041 + insn->sec, insn->offset); 1042 + return -1; 1043 + } 1044 + 1045 + break; 1046 + 1047 + case OP_SRC_ADD: 1048 + if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) { 1049 + 1050 + /* add imm, %rsp */ 1051 + state->stack_size -= op->src.offset; 1052 + if (cfa->base == CFI_SP) 1053 + cfa->offset -= op->src.offset; 1054 + break; 1055 + } 1056 + 1057 + if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) { 1058 + 1059 + /* lea disp(%rbp), %rsp */ 1060 + state->stack_size = -(op->src.offset + regs[CFI_BP].offset); 1061 + break; 1062 + } 1063 + 1064 + if (op->dest.reg != CFI_BP && op->src.reg == CFI_SP && 1065 + cfa->base == CFI_SP) { 1066 + 1067 + /* drap: lea disp(%rsp), %drap */ 1068 + state->drap_reg = op->dest.reg; 1069 + break; 1070 + } 1071 + 1072 + if (state->drap && op->dest.reg == CFI_SP && 1073 + op->src.reg == state->drap_reg) { 1074 + 1075 + /* drap: lea disp(%drap), %rsp */ 1076 + cfa->base = CFI_SP; 1077 + cfa->offset = state->stack_size = -op->src.offset; 1078 + state->drap_reg = CFI_UNDEFINED; 1079 + state->drap = false; 1080 + break; 1081 + } 1082 + 1083 + if (op->dest.reg == state->cfa.base) { 1084 + WARN_FUNC("unsupported stack register modification", 1085 + insn->sec, insn->offset); 1086 + return -1; 1087 + } 1088 + 1089 + break; 1090 + 1091 + case OP_SRC_AND: 1092 + if (op->dest.reg != CFI_SP || 1093 + (state->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) || 1094 + (state->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) { 1095 + WARN_FUNC("unsupported stack pointer realignment", 1096 + insn->sec, insn->offset); 1097 + return -1; 1098 + } 1099 + 1100 + if (state->drap_reg != CFI_UNDEFINED) { 1101 + /* drap: and imm, %rsp */ 1102 + cfa->base = state->drap_reg; 1103 + cfa->offset = state->stack_size = 0; 1104 + state->drap = true; 1105 + 1106 + } 1107 + 1108 + /* 1109 + * Older versions of GCC (4.8ish) realign the stack 1110 + * without DRAP, with a frame pointer. 1111 + */ 1112 + 1113 + break; 1114 + 1115 + case OP_SRC_POP: 1116 + if (!state->drap && op->dest.type == OP_DEST_REG && 1117 + op->dest.reg == cfa->base) { 1118 + 1119 + /* pop %rbp */ 1120 + cfa->base = CFI_SP; 1121 + } 1122 + 1123 + if (regs[op->dest.reg].offset == -state->stack_size) { 1124 + 1125 + if (state->drap && cfa->base == CFI_BP_INDIRECT && 1126 + op->dest.type == OP_DEST_REG && 1127 + op->dest.reg == state->drap_reg) { 1128 + 1129 + /* drap: pop %drap */ 1130 + cfa->base = state->drap_reg; 1131 + cfa->offset = 0; 1132 + } 1133 + 1134 + restore_reg(state, op->dest.reg); 1135 + } 1136 + 1137 + state->stack_size -= 8; 1138 + if (cfa->base == CFI_SP) 1139 + cfa->offset -= 8; 1140 + 1141 + break; 1142 + 1143 + case OP_SRC_REG_INDIRECT: 1144 + if (state->drap && op->src.reg == CFI_BP && 1145 + op->src.offset == regs[op->dest.reg].offset) { 1146 + 1147 + /* drap: mov disp(%rbp), %reg */ 1148 + if (op->dest.reg == state->drap_reg) { 1149 + cfa->base = state->drap_reg; 1150 + cfa->offset = 0; 1151 + } 1152 + 1153 + restore_reg(state, op->dest.reg); 1154 + 1155 + } else if (op->src.reg == cfa->base && 1156 + op->src.offset == regs[op->dest.reg].offset + cfa->offset) { 1157 + 1158 + /* mov disp(%rbp), %reg */ 1159 + /* mov disp(%rsp), %reg */ 1160 + restore_reg(state, op->dest.reg); 1161 + } 1162 + 1163 + break; 1164 + 1165 + default: 1166 + WARN_FUNC("unknown stack-related instruction", 1167 + insn->sec, insn->offset); 1168 + return -1; 1169 + } 1170 + 1171 + break; 1172 + 1173 + case OP_DEST_PUSH: 1174 + state->stack_size += 8; 1175 + if (cfa->base == CFI_SP) 1176 + cfa->offset += 8; 1177 + 1178 + if (op->src.type != OP_SRC_REG) 1179 + break; 1180 + 1181 + if (state->drap) { 1182 + if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) { 1183 + 1184 + /* drap: push %drap */ 1185 + cfa->base = CFI_BP_INDIRECT; 1186 + cfa->offset = -state->stack_size; 1187 + 1188 + /* save drap so we know when to undefine it */ 1189 + save_reg(state, op->src.reg, CFI_CFA, -state->stack_size); 1190 + 1191 + } else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) { 1192 + 1193 + /* drap: push %rbp */ 1194 + state->stack_size = 0; 1195 + 1196 + } else if (regs[op->src.reg].base == CFI_UNDEFINED) { 1197 + 1198 + /* drap: push %reg */ 1199 + save_reg(state, op->src.reg, CFI_BP, -state->stack_size); 1200 + } 1201 + 1202 + } else { 1203 + 1204 + /* push %reg */ 1205 + save_reg(state, op->src.reg, CFI_CFA, -state->stack_size); 1206 + } 1207 + 1208 + /* detect when asm code uses rbp as a scratch register */ 1209 + if (!nofp && insn->func && op->src.reg == CFI_BP && 1210 + cfa->base != CFI_BP) 1211 + state->bp_scratch = true; 1212 + break; 1213 + 1214 + case OP_DEST_REG_INDIRECT: 1215 + 1216 + if (state->drap) { 1217 + if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) { 1218 + 1219 + /* drap: mov %drap, disp(%rbp) */ 1220 + cfa->base = CFI_BP_INDIRECT; 1221 + cfa->offset = op->dest.offset; 1222 + 1223 + /* save drap so we know when to undefine it */ 1224 + save_reg(state, op->src.reg, CFI_CFA, op->dest.offset); 1225 + } 1226 + 1227 + else if (regs[op->src.reg].base == CFI_UNDEFINED) { 1228 + 1229 + /* drap: mov reg, disp(%rbp) */ 1230 + save_reg(state, op->src.reg, CFI_BP, op->dest.offset); 1231 + } 1232 + 1233 + } else if (op->dest.reg == cfa->base) { 1234 + 1235 + /* mov reg, disp(%rbp) */ 1236 + /* mov reg, disp(%rsp) */ 1237 + save_reg(state, op->src.reg, CFI_CFA, 1238 + op->dest.offset - state->cfa.offset); 1239 + } 1240 + 1241 + break; 1242 + 1243 + case OP_DEST_LEAVE: 1244 + if ((!state->drap && cfa->base != CFI_BP) || 1245 + (state->drap && cfa->base != state->drap_reg)) { 1246 + WARN_FUNC("leave instruction with modified stack frame", 1247 + insn->sec, insn->offset); 1248 + return -1; 1249 + } 1250 + 1251 + /* leave (mov %rbp, %rsp; pop %rbp) */ 1252 + 1253 + state->stack_size = -state->regs[CFI_BP].offset - 8; 1254 + restore_reg(state, CFI_BP); 1255 + 1256 + if (!state->drap) { 1257 + cfa->base = CFI_SP; 1258 + cfa->offset -= 8; 1259 + } 1260 + 1261 + break; 1262 + 1263 + case OP_DEST_MEM: 1264 + if (op->src.type != OP_SRC_POP) { 1265 + WARN_FUNC("unknown stack-related memory operation", 1266 + insn->sec, insn->offset); 1267 + return -1; 1268 + } 1269 + 1270 + /* pop mem */ 1271 + state->stack_size -= 8; 1272 + if (cfa->base == CFI_SP) 1273 + cfa->offset -= 8; 1274 + 1275 + break; 1276 + 1277 + default: 1278 + WARN_FUNC("unknown stack-related instruction", 1279 + insn->sec, insn->offset); 1280 + return -1; 1281 + } 1282 + 1283 + return 0; 1284 + } 1285 + 1286 + static bool insn_state_match(struct instruction *insn, struct insn_state *state) 1287 + { 1288 + struct insn_state *state1 = &insn->state, *state2 = state; 1289 + int i; 1290 + 1291 + if (memcmp(&state1->cfa, &state2->cfa, sizeof(state1->cfa))) { 1292 + WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d", 1293 + insn->sec, insn->offset, 1294 + state1->cfa.base, state1->cfa.offset, 1295 + state2->cfa.base, state2->cfa.offset); 1296 + 1297 + } else if (memcmp(&state1->regs, &state2->regs, sizeof(state1->regs))) { 1298 + for (i = 0; i < CFI_NUM_REGS; i++) { 1299 + if (!memcmp(&state1->regs[i], &state2->regs[i], 1300 + sizeof(struct cfi_reg))) 1301 + continue; 1302 + 1303 + WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d", 1304 + insn->sec, insn->offset, 1305 + i, state1->regs[i].base, state1->regs[i].offset, 1306 + i, state2->regs[i].base, state2->regs[i].offset); 1307 + break; 1308 + } 1309 + 1310 + } else if (state1->drap != state2->drap || 1311 + (state1->drap && state1->drap_reg != state2->drap_reg)) { 1312 + WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)", 1313 + insn->sec, insn->offset, 1314 + state1->drap, state1->drap_reg, 1315 + state2->drap, state2->drap_reg); 1316 + 1317 + } else 1318 + return true; 1319 + 1320 + return false; 929 1321 } 930 1322 931 1323 /* ··· 1336 924 * each instruction and validate all the rules described in 1337 925 * tools/objtool/Documentation/stack-validation.txt. 1338 926 */ 1339 - static int validate_branch(struct objtool_file *file, 1340 - struct instruction *first, unsigned char first_state) 927 + static int validate_branch(struct objtool_file *file, struct instruction *first, 928 + struct insn_state state) 1341 929 { 1342 930 struct alternative *alt; 1343 931 struct instruction *insn; 1344 932 struct section *sec; 1345 933 struct symbol *func = NULL; 1346 - unsigned char state; 1347 934 int ret; 1348 935 1349 936 insn = first; 1350 937 sec = insn->sec; 1351 - state = first_state; 1352 938 1353 939 if (insn->alt_group && list_empty(&insn->alts)) { 1354 940 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group", 1355 941 sec, insn->offset); 1356 - return 1; 942 + return -1; 1357 943 } 1358 944 1359 945 while (1) { ··· 1361 951 func->name, insn->func->name); 1362 952 return 1; 1363 953 } 1364 - 1365 - func = insn->func; 1366 954 } 1367 955 956 + func = insn->func; 957 + 1368 958 if (insn->visited) { 1369 - if (frame_state(insn->state) != frame_state(state)) { 1370 - WARN_FUNC("frame pointer state mismatch", 1371 - sec, insn->offset); 959 + if (!!insn_state_match(insn, &state)) 1372 960 return 1; 1373 - } 1374 961 1375 962 return 0; 1376 963 } 1377 964 1378 - insn->visited = true; 1379 965 insn->state = state; 966 + 967 + insn->visited = true; 1380 968 1381 969 list_for_each_entry(alt, &insn->alts, list) { 1382 970 ret = validate_branch(file, alt->insn, state); ··· 1384 976 1385 977 switch (insn->type) { 1386 978 1387 - case INSN_FP_SAVE: 1388 - if (!nofp) { 1389 - if (state & STATE_FP_SAVED) { 1390 - WARN_FUNC("duplicate frame pointer save", 1391 - sec, insn->offset); 1392 - return 1; 1393 - } 1394 - state |= STATE_FP_SAVED; 1395 - } 1396 - break; 1397 - 1398 - case INSN_FP_SETUP: 1399 - if (!nofp) { 1400 - if (state & STATE_FP_SETUP) { 1401 - WARN_FUNC("duplicate frame pointer setup", 1402 - sec, insn->offset); 1403 - return 1; 1404 - } 1405 - state |= STATE_FP_SETUP; 1406 - } 1407 - break; 1408 - 1409 - case INSN_FP_RESTORE: 1410 - if (!nofp) { 1411 - if (has_valid_stack_frame(insn)) 1412 - state &= ~STATE_FP_SETUP; 1413 - 1414 - state &= ~STATE_FP_SAVED; 1415 - } 1416 - break; 1417 - 1418 979 case INSN_RETURN: 1419 - if (!nofp && has_modified_stack_frame(insn)) { 1420 - WARN_FUNC("return without frame pointer restore", 980 + if (func && has_modified_stack_frame(&state)) { 981 + WARN_FUNC("return with modified stack frame", 1421 982 sec, insn->offset); 1422 983 return 1; 1423 984 } 985 + 986 + if (state.bp_scratch) { 987 + WARN("%s uses BP as a scratch register", 988 + insn->func->name); 989 + return 1; 990 + } 991 + 1424 992 return 0; 1425 993 1426 994 case INSN_CALL: 1427 - if (is_fentry_call(insn)) { 1428 - state |= STATE_FENTRY; 995 + if (is_fentry_call(insn)) 1429 996 break; 1430 - } 1431 997 1432 998 ret = dead_end_function(file, insn->call_dest); 1433 999 if (ret == 1) ··· 1411 1029 1412 1030 /* fallthrough */ 1413 1031 case INSN_CALL_DYNAMIC: 1414 - if (!nofp && !has_valid_stack_frame(insn)) { 1032 + if (!nofp && func && !has_valid_stack_frame(&state)) { 1415 1033 WARN_FUNC("call without frame pointer save/setup", 1416 1034 sec, insn->offset); 1417 1035 return 1; ··· 1425 1043 state); 1426 1044 if (ret) 1427 1045 return 1; 1428 - } else if (has_modified_stack_frame(insn)) { 1429 - WARN_FUNC("sibling call from callable instruction with changed frame pointer", 1046 + } else if (func && has_modified_stack_frame(&state)) { 1047 + WARN_FUNC("sibling call from callable instruction with modified stack frame", 1430 1048 sec, insn->offset); 1431 1049 return 1; 1432 1050 } /* else it's a sibling call */ ··· 1437 1055 break; 1438 1056 1439 1057 case INSN_JUMP_DYNAMIC: 1440 - if (list_empty(&insn->alts) && 1441 - has_modified_stack_frame(insn)) { 1442 - WARN_FUNC("sibling call from callable instruction with changed frame pointer", 1058 + if (func && list_empty(&insn->alts) && 1059 + has_modified_stack_frame(&state)) { 1060 + WARN_FUNC("sibling call from callable instruction with modified stack frame", 1443 1061 sec, insn->offset); 1444 1062 return 1; 1445 1063 } 1446 1064 1447 1065 return 0; 1066 + 1067 + case INSN_CONTEXT_SWITCH: 1068 + if (func) { 1069 + WARN_FUNC("unsupported instruction in callable function", 1070 + sec, insn->offset); 1071 + return 1; 1072 + } 1073 + return 0; 1074 + 1075 + case INSN_STACK: 1076 + if (update_insn_state(insn, &state)) 1077 + return -1; 1078 + 1079 + break; 1448 1080 1449 1081 default: 1450 1082 break; ··· 1490 1094 "__ubsan_handle_builtin_unreachable")); 1491 1095 } 1492 1096 1493 - static bool ignore_unreachable_insn(struct symbol *func, 1494 - struct instruction *insn) 1097 + static bool ignore_unreachable_insn(struct instruction *insn) 1495 1098 { 1496 1099 int i; 1497 1100 1498 - if (insn->type == INSN_NOP) 1101 + if (insn->ignore || insn->type == INSN_NOP) 1102 + return true; 1103 + 1104 + /* 1105 + * Ignore any unused exceptions. This can happen when a whitelisted 1106 + * function has an exception table entry. 1107 + */ 1108 + if (!strcmp(insn->sec->name, ".fixup")) 1499 1109 return true; 1500 1110 1501 1111 /* ··· 1510 1108 * 1511 1109 * End the search at 5 instructions to avoid going into the weeds. 1512 1110 */ 1111 + if (!insn->func) 1112 + return false; 1513 1113 for (i = 0; i < 5; i++) { 1514 1114 1515 1115 if (is_kasan_insn(insn) || is_ubsan_insn(insn)) ··· 1522 1118 continue; 1523 1119 } 1524 1120 1525 - if (insn->offset + insn->len >= func->offset + func->len) 1121 + if (insn->offset + insn->len >= insn->func->offset + insn->func->len) 1526 1122 break; 1527 1123 insn = list_next_entry(insn, list); 1528 1124 } ··· 1535 1131 struct section *sec; 1536 1132 struct symbol *func; 1537 1133 struct instruction *insn; 1134 + struct insn_state state; 1538 1135 int ret, warnings = 0; 1539 1136 1540 - list_for_each_entry(sec, &file->elf->sections, list) { 1137 + clear_insn_state(&state); 1138 + 1139 + state.cfa = initial_func_cfi.cfa; 1140 + memcpy(&state.regs, &initial_func_cfi.regs, 1141 + CFI_NUM_REGS * sizeof(struct cfi_reg)); 1142 + state.stack_size = initial_func_cfi.cfa.offset; 1143 + 1144 + for_each_sec(file, sec) { 1541 1145 list_for_each_entry(func, &sec->symbol_list, list) { 1542 1146 if (func->type != STT_FUNC) 1543 1147 continue; 1544 1148 1545 1149 insn = find_insn(file, sec, func->offset); 1546 - if (!insn) 1150 + if (!insn || insn->ignore) 1547 1151 continue; 1548 1152 1549 - ret = validate_branch(file, insn, 0); 1153 + ret = validate_branch(file, insn, state); 1550 1154 warnings += ret; 1551 - } 1552 - } 1553 - 1554 - list_for_each_entry(sec, &file->elf->sections, list) { 1555 - list_for_each_entry(func, &sec->symbol_list, list) { 1556 - if (func->type != STT_FUNC) 1557 - continue; 1558 - 1559 - func_for_each_insn(file, func, insn) { 1560 - if (insn->visited) 1561 - continue; 1562 - 1563 - insn->visited = true; 1564 - 1565 - if (file->ignore_unreachables || warnings || 1566 - ignore_unreachable_insn(func, insn)) 1567 - continue; 1568 - 1569 - /* 1570 - * gcov produces a lot of unreachable 1571 - * instructions. If we get an unreachable 1572 - * warning and the file has gcov enabled, just 1573 - * ignore it, and all other such warnings for 1574 - * the file. 1575 - */ 1576 - if (!file->ignore_unreachables && 1577 - gcov_enabled(file)) { 1578 - file->ignore_unreachables = true; 1579 - continue; 1580 - } 1581 - 1582 - WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset); 1583 - warnings++; 1584 - } 1585 1155 } 1586 1156 } 1587 1157 1588 1158 return warnings; 1589 1159 } 1590 1160 1591 - static int validate_uncallable_instructions(struct objtool_file *file) 1161 + static int validate_reachable_instructions(struct objtool_file *file) 1592 1162 { 1593 1163 struct instruction *insn; 1594 - int warnings = 0; 1164 + 1165 + if (file->ignore_unreachables) 1166 + return 0; 1595 1167 1596 1168 for_each_insn(file, insn) { 1597 - if (!insn->visited && insn->type == INSN_RETURN) { 1598 - WARN_FUNC("return instruction outside of a callable function", 1599 - insn->sec, insn->offset); 1600 - warnings++; 1601 - } 1169 + if (insn->visited || ignore_unreachable_insn(insn)) 1170 + continue; 1171 + 1172 + /* 1173 + * gcov produces a lot of unreachable instructions. If we get 1174 + * an unreachable warning and the file has gcov enabled, just 1175 + * ignore it, and all other such warnings for the file. Do 1176 + * this here because this is an expensive function. 1177 + */ 1178 + if (gcov_enabled(file)) 1179 + return 0; 1180 + 1181 + WARN_FUNC("unreachable instruction", insn->sec, insn->offset); 1182 + return 1; 1602 1183 } 1603 1184 1604 - return warnings; 1185 + return 0; 1605 1186 } 1606 1187 1607 1188 static void cleanup(struct objtool_file *file) ··· 1615 1226 nofp = _nofp; 1616 1227 1617 1228 file.elf = elf_open(objname); 1618 - if (!file.elf) { 1619 - fprintf(stderr, "error reading elf file %s\n", objname); 1229 + if (!file.elf) 1620 1230 return 1; 1621 - } 1622 1231 1623 1232 INIT_LIST_HEAD(&file.insn_list); 1624 1233 hash_init(file.insn_hash); ··· 1625 1238 file.ignore_unreachables = false; 1626 1239 file.c_file = find_section_by_name(file.elf, ".comment"); 1627 1240 1241 + arch_initial_func_cfi_state(&initial_func_cfi); 1242 + 1628 1243 ret = decode_sections(&file); 1629 1244 if (ret < 0) 1630 1245 goto out; 1631 1246 warnings += ret; 1247 + 1248 + if (list_empty(&file.insn_list)) 1249 + goto out; 1632 1250 1633 1251 ret = validate_functions(&file); 1634 1252 if (ret < 0) 1635 1253 goto out; 1636 1254 warnings += ret; 1637 1255 1638 - ret = validate_uncallable_instructions(&file); 1639 - if (ret < 0) 1640 - goto out; 1641 - warnings += ret; 1256 + if (!warnings) { 1257 + ret = validate_reachable_instructions(&file); 1258 + if (ret < 0) 1259 + goto out; 1260 + warnings += ret; 1261 + } 1642 1262 1643 1263 out: 1644 1264 cleanup(&file);
+17 -2
tools/objtool/check.h
··· 20 20 21 21 #include <stdbool.h> 22 22 #include "elf.h" 23 + #include "cfi.h" 23 24 #include "arch.h" 24 25 #include <linux/hashtable.h> 26 + 27 + struct insn_state { 28 + struct cfi_reg cfa; 29 + struct cfi_reg regs[CFI_NUM_REGS]; 30 + int stack_size; 31 + bool bp_scratch; 32 + bool drap; 33 + int drap_reg; 34 + }; 25 35 26 36 struct instruction { 27 37 struct list_head list; 28 38 struct hlist_node hash; 29 39 struct section *sec; 30 40 unsigned long offset; 31 - unsigned int len, state; 41 + unsigned int len; 32 42 unsigned char type; 33 43 unsigned long immediate; 34 - bool alt_group, visited, dead_end; 44 + bool alt_group, visited, dead_end, ignore; 35 45 struct symbol *call_dest; 36 46 struct instruction *jump_dest; 37 47 struct list_head alts; 38 48 struct symbol *func; 49 + struct stack_op stack_op; 50 + struct insn_state state; 39 51 }; 40 52 41 53 struct objtool_file { ··· 59 47 }; 60 48 61 49 int check(const char *objname, bool nofp); 50 + 51 + #define for_each_insn(file, insn) \ 52 + list_for_each_entry(insn, &file->insn_list, list) 62 53 63 54 #endif /* _CHECK_H */
+28 -31
tools/objtool/elf.c
··· 37 37 #define ELF_C_READ_MMAP ELF_C_READ 38 38 #endif 39 39 40 + #define WARN_ELF(format, ...) \ 41 + WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) 42 + 40 43 struct section *find_section_by_name(struct elf *elf, const char *name) 41 44 { 42 45 struct section *sec; ··· 142 139 int i; 143 140 144 141 if (elf_getshdrnum(elf->elf, &sections_nr)) { 145 - perror("elf_getshdrnum"); 142 + WARN_ELF("elf_getshdrnum"); 146 143 return -1; 147 144 } 148 145 149 146 if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 150 - perror("elf_getshdrstrndx"); 147 + WARN_ELF("elf_getshdrstrndx"); 151 148 return -1; 152 149 } 153 150 ··· 168 165 169 166 s = elf_getscn(elf->elf, i); 170 167 if (!s) { 171 - perror("elf_getscn"); 168 + WARN_ELF("elf_getscn"); 172 169 return -1; 173 170 } 174 171 175 172 sec->idx = elf_ndxscn(s); 176 173 177 174 if (!gelf_getshdr(s, &sec->sh)) { 178 - perror("gelf_getshdr"); 175 + WARN_ELF("gelf_getshdr"); 179 176 return -1; 180 177 } 181 178 182 179 sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 183 180 if (!sec->name) { 184 - perror("elf_strptr"); 181 + WARN_ELF("elf_strptr"); 185 182 return -1; 186 183 } 187 184 188 - sec->elf_data = elf_getdata(s, NULL); 189 - if (!sec->elf_data) { 190 - perror("elf_getdata"); 185 + sec->data = elf_getdata(s, NULL); 186 + if (!sec->data) { 187 + WARN_ELF("elf_getdata"); 191 188 return -1; 192 189 } 193 190 194 - if (sec->elf_data->d_off != 0 || 195 - sec->elf_data->d_size != sec->sh.sh_size) { 191 + if (sec->data->d_off != 0 || 192 + sec->data->d_size != sec->sh.sh_size) { 196 193 WARN("unexpected data attributes for %s", sec->name); 197 194 return -1; 198 195 } 199 196 200 - sec->data = (unsigned long)sec->elf_data->d_buf; 201 - sec->len = sec->elf_data->d_size; 197 + sec->len = sec->data->d_size; 202 198 } 203 199 204 200 /* sanity check, one more call to elf_nextscn() should return NULL */ ··· 234 232 235 233 sym->idx = i; 236 234 237 - if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) { 238 - perror("gelf_getsym"); 235 + if (!gelf_getsym(symtab->data, i, &sym->sym)) { 236 + WARN_ELF("gelf_getsym"); 239 237 goto err; 240 238 } 241 239 242 240 sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 243 241 sym->sym.st_name); 244 242 if (!sym->name) { 245 - perror("elf_strptr"); 243 + WARN_ELF("elf_strptr"); 246 244 goto err; 247 245 } 248 246 ··· 324 322 } 325 323 memset(rela, 0, sizeof(*rela)); 326 324 327 - if (!gelf_getrela(sec->elf_data, i, &rela->rela)) { 328 - perror("gelf_getrela"); 325 + if (!gelf_getrela(sec->data, i, &rela->rela)) { 326 + WARN_ELF("gelf_getrela"); 329 327 return -1; 330 328 } 331 329 ··· 364 362 365 363 INIT_LIST_HEAD(&elf->sections); 366 364 367 - elf->name = strdup(name); 368 - if (!elf->name) { 369 - perror("strdup"); 370 - goto err; 371 - } 372 - 373 365 elf->fd = open(name, O_RDONLY); 374 366 if (elf->fd == -1) { 375 367 perror("open"); ··· 372 376 373 377 elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL); 374 378 if (!elf->elf) { 375 - perror("elf_begin"); 379 + WARN_ELF("elf_begin"); 376 380 goto err; 377 381 } 378 382 379 383 if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 380 - perror("gelf_getehdr"); 384 + WARN_ELF("gelf_getehdr"); 381 385 goto err; 382 386 } 383 387 ··· 403 407 struct symbol *sym, *tmpsym; 404 408 struct rela *rela, *tmprela; 405 409 410 + if (elf->elf) 411 + elf_end(elf->elf); 412 + 413 + if (elf->fd > 0) 414 + close(elf->fd); 415 + 406 416 list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 407 417 list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 408 418 list_del(&sym->list); ··· 423 421 list_del(&sec->list); 424 422 free(sec); 425 423 } 426 - if (elf->name) 427 - free(elf->name); 428 - if (elf->fd > 0) 429 - close(elf->fd); 430 - if (elf->elf) 431 - elf_end(elf->elf); 424 + 432 425 free(elf); 433 426 }
+3 -3
tools/objtool/elf.h
··· 37 37 DECLARE_HASHTABLE(rela_hash, 16); 38 38 struct section *base, *rela; 39 39 struct symbol *sym; 40 - Elf_Data *elf_data; 40 + Elf_Data *data; 41 41 char *name; 42 42 int idx; 43 - unsigned long data; 44 43 unsigned int len; 45 44 }; 46 45 ··· 85 86 struct symbol *find_containing_func(struct section *sec, unsigned long offset); 86 87 void elf_close(struct elf *elf); 87 88 88 - 89 + #define for_each_sec(file, sec) \ 90 + list_for_each_entry(sec, &file->elf->sections, list) 89 91 90 92 #endif /* _OBJTOOL_ELF_H */
+3 -3
tools/objtool/special.c
··· 91 91 alt->jump_or_nop = entry->jump_or_nop; 92 92 93 93 if (alt->group) { 94 - alt->orig_len = *(unsigned char *)(sec->data + offset + 94 + alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset + 95 95 entry->orig_len); 96 - alt->new_len = *(unsigned char *)(sec->data + offset + 96 + alt->new_len = *(unsigned char *)(sec->data->d_buf + offset + 97 97 entry->new_len); 98 98 } 99 99 100 100 if (entry->feature) { 101 101 unsigned short feature; 102 102 103 - feature = *(unsigned short *)(sec->data + offset + 103 + feature = *(unsigned short *)(sec->data->d_buf + offset + 104 104 entry->feature); 105 105 106 106 /*
+10
tools/objtool/warn.h
··· 18 18 #ifndef _WARN_H 19 19 #define _WARN_H 20 20 21 + #include <stdlib.h> 22 + #include <string.h> 23 + #include <sys/types.h> 24 + #include <sys/stat.h> 25 + #include <fcntl.h> 26 + #include "elf.h" 27 + 21 28 extern const char *objname; 22 29 23 30 static inline char *offstr(struct section *sec, unsigned long offset) ··· 63 56 WARN("%s: " format, _str, ##__VA_ARGS__); \ 64 57 free(_str); \ 65 58 }) 59 + 60 + #define WARN_ELF(format, ...) \ 61 + WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) 66 62 67 63 #endif /* _WARN_H */