#! /bin/bash # (c) 2015, Quentin Casasnovas global obj := $1 file $(obj) | grep -q ELF || shell {echo "$(obj) is not and ELF file." !1 > !2 ; exit 0} # Bail out early if there isn't an __ex_table section in this object file. objdump -hj __ex_table $(obj) !2 > /dev/null > /dev/null test $Status -ne 0 && exit 0 global white_list := '.text,.fixup' global suspicious_relocs := $[objdump -rj __ex_table $(obj) | tail -n +6 | grep -v $[eval echo -e{$(white_list)}] | awk '{print $3}] # No suspicious relocs in __ex_table, jobs a good'un test -z $(suspicious_relocs) && exit 0 # After this point, something is seriously wrong since we just found out we # have some relocations in __ex_table which point to sections which aren't # white listed. If you're adding a new section in the Linux kernel, and # you're expecting this section to contain code which can fault (i.e. the # __ex_table relocation to your new section is expected), simply add your # new section to the white_list variable above. If not, you're probably # doing something wrong and the rest of this code is just trying to print # you more information about it. proc find_section_offset_from_symbol { eval $[objdump -t $(obj) | grep $(1) | sed 's/\([0-9a-f]\+\) .\{7\} \([^ \t]\+\).*/section="\2"; section_offset="0x\1" /] # addr2line takes addresses in hexadecimal... global section_offset := $[printf "0x%016x" $( ${section_offset} + $2 )] } proc find_symbol_and_offset_from_reloc { # Extract symbol and offset from the objdump output eval $[echo $reloc | sed 's/\([^+]\+\)+\?\(0x[0-9a-f]\+\)\?/symbol="\1"; symbol_offset="\2"/] # When the relocation points to the begining of a symbol or section, it # won't print the offset since it is zero. if test -z $(symbol_offset) { global symbol_offset := '0x0' } } proc find_alt_replacement_target { # The target of the .altinstr_replacement is the relocation just before # the .altinstr_replacement one. eval $[objdump -rj .altinstructions $(obj) | grep -B1 "$(section)+$(section_offset)" | head -n1 | awk '{print $3}' | sed 's/\([^+]\+\)+\(0x[0-9a-f]\+\)/alt_target_section="\1"; alt_target_offset="\2"/] } proc handle_alt_replacement_reloc { # This will define alt_target_section and alt_target_section_offset find_alt_replacement_target $(section) $(section_offset) echo "Error: found a reference to .altinstr_replacement in __ex_table:" addr2line -fip -j $(alt_target_section) -e $(obj) $(alt_target_offset) | awk '{print "\t" $0}' global error := 'true' } proc is_executable_section { objdump -hwj $(section) $(obj) | grep -q CODE return $? } proc handle_suspicious_generic_reloc { if is_executable_section $(section) { # We've got a relocation to a non white listed _executable_ # section, print a warning so the developper adds the section to # the white list or fix his code. We try to pretty-print the file # and line number where that relocation was added. echo "Warning: found a reference to section \"$(section)\" in __ex_table:" addr2line -fip -j $(section) -e $(obj) $(section_offset) | awk '{print "\t" $0}' } else { # Something is definitively wrong here since we've got a relocation # to a non-executable section, there's no way this would ever be # running in the kernel. echo "Error: found a reference to non-executable section \"$(section)\" in __ex_table at offset $(section_offset)" global error := 'true' } } proc handle_suspicious_reloc { matchstr $(section) { ".altinstr_replacement" { handle_alt_replacement_reloc $(section) $(section_offset) } * { handle_suspicious_generic_reloc $(section) $(section_offset) } } } proc diagnose { for reloc in [$(suspicious_relocs)] { # Let's find out where the target of the relocation in __ex_table # is, this will define ${symbol} and ${symbol_offset} find_symbol_and_offset_from_reloc $(reloc) # When there's a global symbol at the place of the relocation, # objdump will use it instead of giving us a section+offset, so # let's find out which section is this symbol in and the total # offset withing that section. find_section_offset_from_symbol $(symbol) $(symbol_offset) # In this case objdump was presenting us with a reloc to a symbol # rather than a section. Now that we've got the actual section, # we can skip it if it's in the white_list. if test -z $[ echo $section | grep -v $[eval echo -e{$(white_list)}]] { continue; } # Will either print a warning if the relocation happens to be in a # section we do not know but has executable bit set, or error out. handle_suspicious_reloc } } proc check_debug_info { objdump -hj .debug_info $(obj) !2 > /dev/null > /dev/null || echo -e "$(obj) does not contain debug information, the addr2line output will be limited.\n" \ "Recompile $(obj) with CONFIG_DEBUG_INFO to get a more useful output." } check_debug_info diagnose if test $(error) { exit 1 } exit 0 (CommandList children: [ (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:obj) op:Equal rhs:{($ VSub_Number "$1")} spids:[7])] spids: [7] ) (AndOr children: [ (Pipeline children: [(C {(file)} {(${ VSub_Name obj)}) (C {(grep)} {(-q)} {(ELF)})] negated: False ) (Subshell child: (CommandList children: [ (Sentence child: (SimpleCommand words: [{(echo)} {(DQ (${ VSub_Name obj) (" is not and ELF file."))}] redirects: [(Redir op_id:Redir_GreatAnd fd:1 arg_word:{(2)} spids:[37])] ) terminator: ) (C {(exit)} {(0)}) ] ) spids: [27 45] ) ] op_id: Op_DPipe ) (SimpleCommand words: [{(objdump)} {(-hj)} {(__ex_table)} {(${ VSub_Name obj)}] redirects: [ (Redir op_id:Redir_Great fd:2 arg_word:{(/dev/null)} spids:[61]) (Redir op_id:Redir_Great fd:-1 arg_word:{(/dev/null)} spids:[65]) ] ) (AndOr children: [ (C {(Lit_Other "[")} {($ VSub_QMark "$?")} {(-ne)} {(0)} {(Lit_Other "]")}) (C {(exit)} {(0)}) ] op_id: Op_DAmp ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:white_list) op: Equal rhs: {(.text) (Lit_Comma ",") (.fixup)} spids: [86] ) ] spids: [86] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:suspicious_relocs) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(objdump)} {(-rj)} {(__ex_table)} {(${ VSub_Name obj)}) (C {(tail)} {(-n)} {(Lit_Other "+") (6)}) (C {(grep)} {(-v)} { (CommandSubPart command_list: (CommandList children: [ (C {(eval)} {(echo)} {(-e) (Lit_LBrace "{") (${ VSub_Name white_list) (Lit_RBrace "}") } ) ] ) left_token: spids: [120 131] ) } ) (C {(awk)} {(SQ <"{print $3}">)}) ] negated: False ) ] ) left_token: spids: [93 140] ) } spids: [92] ) ] spids: [92] ) (AndOr children: [ (C {(Lit_Other "[")} {(-z)} {(DQ (${ VSub_Name suspicious_relocs))} {(Lit_Other "]")}) (C {(exit)} {(0)}) ] op_id: Op_DAmp ) (FuncDef name: find_section_offset_from_symbol body: (BraceGroup children: [ (C {(eval)} { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(objdump)} {(-t)} {(${ VSub_Name obj)}) (C {(grep)} {(${ VSub_Number 1)}) (C {(sed)} { (SQ < "s/\\([0-9a-f]\\+\\) .\\{7\\} \\([^ \\t]\\+\\).*/section=\"\\2\"; section_offset=\"0x\\1\" /" > ) } ) ] negated: False ) ] ) left_token: spids: [202 226] ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:section_offset) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (C {(printf)} {(DQ ("0x%016x"))} { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{(${ VSub_Name section_offset)}) right: (ArithWord w:{($ VSub_Number "$2")}) ) spids: [242 253] ) } ) ] ) left_token: spids: [235 255] ) } spids: [234] ) ] spids: [234] ) ] spids: [197] ) spids: [191 196] ) (FuncDef name: find_symbol_and_offset_from_reloc body: (BraceGroup children: [ (C {(eval)} { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {($ VSub_Name "$reloc")}) (C {(sed)} { (SQ < "s/\\([^+]\\+\\)+\\?\\(0x[0-9a-f]\\+\\)\\?/symbol=\"\\1\"; symbol_offset=\"\\2\"/" > ) } ) ] negated: False ) ] ) left_token: spids: [275 287] ) } ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} {(DQ (${ VSub_Name symbol_offset))} {(Lit_Other "]")}) terminator: ) ] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:symbol_offset) op: Equal rhs: {(0x0)} spids: [317] ) ] spids: [317] ) ] spids: [-1 314] ) ] spids: [-1 321] ) ] spids: [266] ) spids: [260 265] ) (FuncDef name: find_alt_replacement_target body: (BraceGroup children: [ (C {(eval)} { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(objdump)} {(-rj)} {(.altinstructions)} {(${ VSub_Name obj)}) (C {(grep)} {(-B1)} {(DQ (${ VSub_Name section) ("+") (${ VSub_Name section_offset))} ) (C {(head)} {(-n1)}) (C {(awk)} {(SQ <"{print $3}">)}) (C {(sed)} { (SQ < "s/\\([^+]\\+\\)+\\(0x[0-9a-f]\\+\\)/alt_target_section=\"\\1\"; alt_target_offset=\"\\2\"/" > ) } ) ] negated: False ) ] ) left_token: spids: [345 394] ) } ) ] spids: [332] ) spids: [326 331] ) (FuncDef name: handle_alt_replacement_reloc body: (BraceGroup children: [ (C {(find_alt_replacement_target)} {(${ VSub_Name section)} {(${ VSub_Name section_offset)}) (C {(echo)} {(DQ ("Error: found a reference to .altinstr_replacement in __ex_table:"))}) (Pipeline children: [ (C {(addr2line)} {(-fip)} {(-j)} {(${ VSub_Name alt_target_section)} {(-e)} {(${ VSub_Name obj)} {(${ VSub_Name alt_target_offset)} ) (C {(awk)} {(SQ <"{print \"\\t\" $0}">)}) ] negated: False ) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:error) op:Equal rhs:{(true)} spids:[461])] spids: [461] ) ] spids: [405] ) spids: [399 404] ) (FuncDef name: is_executable_section body: (BraceGroup children: [ (Pipeline children: [ (C {(objdump)} {(-hwj)} {(${ VSub_Name section)} {(${ VSub_Name obj)}) (C {(grep)} {(-q)} {(CODE)}) ] negated: False ) (ControlFlow token: arg_word:{($ VSub_QMark "$?")}) ] spids: [473] ) spids: [467 472] ) (FuncDef name: handle_suspicious_generic_reloc body: (BraceGroup children: [ (If arms: [ (if_arm cond: [ (Sentence child: (C {(is_executable_section)} {(${ VSub_Name section)}) terminator: ) ] action: [ (C {(echo)} { (DQ ("Warning: found a reference to section ") (EscapedLiteralPart token: ) (${ VSub_Name section) (EscapedLiteralPart token:) (" in __ex_table:") ) } ) (Pipeline children: [ (C {(addr2line)} {(-fip)} {(-j)} {(${ VSub_Name section)} {(-e)} {(${ VSub_Name obj)} {(${ VSub_Name section_offset)} ) (C {(awk)} {(SQ <"{print \"\\t\" $0}">)}) ] negated: False ) ] spids: [-1 522] ) ] else_action: [ (C {(echo)} { (DQ ("Error: found a reference to non-executable section ") (EscapedLiteralPart token:) (${ VSub_Name section) (EscapedLiteralPart token:) (" in __ex_table at offset ") (${ VSub_Name section_offset) ) } ) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:error) op:Equal rhs:{(true)} spids:[614])] spids: [614] ) ] spids: [583 618] ) ] spids: [510] ) spids: [504 509] ) (FuncDef name: handle_suspicious_reloc body: (BraceGroup children: [ (Case to_match: {(DQ (${ VSub_Name section))} arms: [ (case_arm pat_list: [{(DQ (.altinstr_replacement))}] action: [ (C {(handle_alt_replacement_reloc)} {(${ VSub_Name section)} {(${ VSub_Name section_offset)} ) ] spids: [644 646 660 -1] ) (case_arm pat_list: [{(Lit_Other "*")}] action: [ (C {(handle_suspicious_generic_reloc)} {(${ VSub_Name section)} {(${ VSub_Name section_offset)} ) ] spids: [663 664 678 -1] ) ] spids: [632 640 681] ) ] spids: [629] ) spids: [623 628] ) (FuncDef name: diagnose body: (BraceGroup children: [ (ForEach iter_name: reloc iter_words: [{(${ VSub_Name suspicious_relocs)}] do_arg_iter: False body: (DoGroup children: [ (C {(find_symbol_and_offset_from_reloc)} {(${ VSub_Name reloc)}) (C {(find_section_offset_from_symbol)} {(${ VSub_Name symbol)} {(${ VSub_Name symbol_offset)} ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {($ VSub_Name "$section")}) (C {(grep)} {(-v)} { (CommandSubPart command_list: (CommandList children: [ (C {(eval)} {(echo)} {(-e) (Lit_LBrace "{") (${ VSub_Name white_list) (Lit_RBrace "}") } ) ] ) left_token: spids: [785 796] ) } ) ] negated: False ) ] ) left_token: spids: [773 797] ) ) } {(Lit_Other "]")} ) terminator: ) ] action: [ (Sentence child: (ControlFlow token:) terminator: ) ] spids: [-1 803] ) ] spids: [-1 810] ) (C {(handle_suspicious_reloc)}) ] spids: [707 825] ) spids: [701 705] ) ] spids: [692] ) spids: [686 691] ) (FuncDef name: check_debug_info body: (BraceGroup children: [ (AndOr children: [ (SimpleCommand words: [{(objdump)} {(-hj)} {(.debug_info)} {(${ VSub_Name obj)}] redirects: [ (Redir op_id: Redir_Great fd: 2 arg_word: {(/dev/null)} spids: [849] ) (Redir op_id: Redir_Great fd: -1 arg_word: {(/dev/null)} spids: [853] ) ] ) (C {(echo)} {(-e)} { (DQ (${ VSub_Name obj) ( " does not contain debug information, the addr2line output will be limited." ) (EscapedLiteralPart token:) ) } {(DQ ("Recompile ") (${ VSub_Name obj) (" with CONFIG_DEBUG_INFO to get a more useful output."))} ) ] op_id: Op_DPipe ) ] spids: [836] ) spids: [830 835] ) (C {(check_debug_info)}) (C {(diagnose)}) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name error))} {(Lit_Other "]")}) terminator: ) ] action: [(C {(exit)} {(1)})] spids: [-1 904] ) ] spids: [-1 911] ) (C {(exit)} {(0)}) ] )