1########################################################################### 2# A module to read ELF binaries 3# 4# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory 5# 6# Written by Andrey Ponomarenko 7# 8# This library is free software; you can redistribute it and/or 9# modify it under the terms of the GNU Lesser General Public 10# License as published by the Free Software Foundation; either 11# version 2.1 of the License, or (at your option) any later version. 12# 13# This library is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16# Lesser General Public License for more details. 17# 18# You should have received a copy of the GNU Lesser General Public 19# License along with this library; if not, write to the Free Software 20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21# MA 02110-1301 USA 22########################################################################### 23use strict; 24 25my %Cache; 26 27my %ELF_BIND = map {$_=>1} ( 28 "WEAK", 29 "GLOBAL" 30); 31 32my %ELF_TYPE = map {$_=>1} ( 33 "FUNC", 34 "IFUNC", 35 "OBJECT", 36 "COMMON" 37); 38 39my %ELF_VIS = map {$_=>1} ( 40 "DEFAULT", 41 "PROTECTED" 42); 43 44sub readline_ELF($) 45{ # read the line of 'readelf' output corresponding to the symbol 46 my @Info = split(/\s+/, $_[0]); 47 # Num: Value Size Type Bind Vis Ndx Name 48 # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 49 # 135: 00000000 0 FUNC GLOBAL DEFAULT UND av_image_fill_pointers@LIBAVUTIL_52 (3) 50 shift(@Info); # spaces 51 shift(@Info); # num 52 53 if($#Info==7) 54 { # UND SYMBOL (N) 55 if($Info[7]=~/\(\d+\)/) { 56 pop(@Info); 57 } 58 } 59 60 if($#Info!=6) 61 { # other lines 62 return (); 63 } 64 return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND"); 65 return () if(not defined $ELF_BIND{$Info[3]}); 66 return () if(not defined $ELF_VIS{$Info[4]}); 67 if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) 68 { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 69 return (); 70 } 71 if($In::Opt{"Target"} eq "symbian") 72 { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll 73 if(index($Info[6], "_._.absent_export_")!=-1) 74 { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll 75 return (); 76 } 77 $Info[6]=~s/\@.+//g; # remove version 78 } 79 if(index($Info[2], "0x") == 0) 80 { # size == 0x3d158 81 $Info[2] = hex($Info[2]); 82 } 83 return @Info; 84} 85 86sub getSONAME($) 87{ 88 my $Path = $_[0]; 89 90 if(defined $Cache{"getSONAME"}{$Path}) { 91 return $Cache{"getSONAME"}{$Path}; 92 } 93 my $Objdump = getCmdPath("objdump"); 94 if(not $Objdump) { 95 exitStatus("Not_Found", "can't find \"objdump\""); 96 } 97 my $TmpDir = $In::Opt{"Tmp"}; 98 my $SonameCmd = "$Objdump -x \"$Path\" 2>$TmpDir/null"; 99 if($In::Opt{"OS"} eq "windows") { 100 $SonameCmd .= " | find \"SONAME\""; 101 } 102 else { 103 $SonameCmd .= " | grep SONAME"; 104 } 105 if(my $Info = `$SonameCmd`) 106 { 107 if($Info=~/SONAME\s+([^\s]+)/) { 108 return ($Cache{"getSONAME"}{$Path} = $1); 109 } 110 } 111 return ($Cache{"getSONAME"}{$Path}=""); 112} 113 114sub getArch_Object($) 115{ 116 my $Path = $_[0]; 117 118 my %MachineType = ( 119 "14C" => "x86", 120 "8664" => "x86_64", 121 "1C0" => "arm", 122 "200" => "ia64" 123 ); 124 125 my %ArchName = ( 126 "s390:31-bit" => "s390", 127 "s390:64-bit" => "s390x", 128 "powerpc:common" => "ppc32", 129 "powerpc:common64" => "ppc64", 130 "i386:x86-64" => "x86_64", 131 "mips:3000" => "mips", 132 "sparc:v8plus" => "sparcv9" 133 ); 134 135 if($In::Opt{"OS"} eq "windows") 136 { 137 my $DumpbinCmd = getCmdPath("dumpbin"); 138 if(not $DumpbinCmd) { 139 exitStatus("Not_Found", "can't find \"dumpbin\""); 140 } 141 142 my $Cmd = $DumpbinCmd." /headers \"$Path\""; 143 my $Out = `$Cmd`; 144 145 if($Out=~/(\w+)\smachine/) 146 { 147 if(my $Type = $MachineType{uc($1)}) 148 { 149 return $Type; 150 } 151 } 152 } 153 elsif($In::Opt{"OS"} eq "macos") 154 { 155 my $OtoolCmd = getCmdPath("otool"); 156 if(not $OtoolCmd) { 157 exitStatus("Not_Found", "can't find \"otool\""); 158 } 159 160 my $Cmd = $OtoolCmd." -hv -arch all \"$Path\""; 161 my $Out = qx/$Cmd/; 162 163 if($Out=~/X86_64/i) { 164 return "x86_64"; 165 } 166 elsif($Out=~/X86/i) { 167 return "x86"; 168 } 169 } 170 else 171 { # linux, bsd, gnu, solaris, ... 172 my $ObjdumpCmd = getCmdPath("objdump"); 173 if(not $ObjdumpCmd) { 174 exitStatus("Not_Found", "can't find \"objdump\""); 175 } 176 177 my $TmpDir = $In::Opt{"Tmp"}; 178 my $Cmd = $ObjdumpCmd." -f \"$Path\" 2>$TmpDir/null"; 179 180 my $Locale = $In::Opt{"Locale"}; 181 if($In::Opt{"OS"} eq "windows") { 182 $Cmd = "set LANG=$Locale & ".$Cmd; 183 } 184 else { 185 $Cmd = "LANG=$Locale ".$Cmd; 186 } 187 my $Out = `$Cmd`; 188 189 if($Out=~/architecture:\s+([\w\-\:]+)/) 190 { 191 my $Arch = $1; 192 if($Arch=~s/\:(.+)//) 193 { 194 my $Suffix = $1; 195 196 if(my $Name = $ArchName{$Arch.":".$Suffix}) 197 { 198 $Arch = $Name; 199 } 200 } 201 202 if($Arch=~/i[3-6]86/) { 203 $Arch = "x86"; 204 } 205 206 if($Arch eq "x86-64") { 207 $Arch = "x86_64"; 208 } 209 210 if($Arch eq "ia64-elf64") { 211 $Arch = "ia64"; 212 } 213 214 return $Arch; 215 } 216 } 217 218 return undef; 219} 220 221sub getArch_GCC($) 222{ 223 my $LVer = $_[0]; 224 225 if(defined $Cache{"getArch_GCC"}{$LVer}) { 226 return $Cache{"getArch_GCC"}{$LVer}; 227 } 228 229 my $GccPath = $In::Opt{"GccPath"}; 230 231 if(not $GccPath) { 232 return undef; 233 } 234 235 my $Arch = undef; 236 237 if(my $Target = $In::Opt{"GccTarget"}) 238 { 239 if($Target=~/x86_64/) { 240 $Arch = "x86_64"; 241 } 242 elsif($Target=~/i[3-6]86/) { 243 $Arch = "x86"; 244 } 245 elsif($Target=~/\Aarm/i) { 246 $Arch = "arm"; 247 } 248 } 249 250 if(not $Arch) 251 { 252 my $TmpDir = $In::Opt{"Tmp"}; 253 my $OrigDir = $In::Opt{"OrigDir"}; 254 255 writeFile($TmpDir."/test.c", "int main(){return 0;}\n"); 256 257 my $Cmd = $GccPath." test.c -o test"; 258 if(my $Opts = getGccOptions($LVer)) 259 { # user-defined options 260 $Cmd .= " ".$Opts; 261 } 262 263 chdir($TmpDir); 264 system($Cmd); 265 chdir($OrigDir); 266 267 my $EX = join_P($TmpDir, "test"); 268 269 if($In::Opt{"OS"} eq "windows") { 270 $EX = join_P($TmpDir, "test.exe"); 271 } 272 273 $Arch = getArch_Object($EX); 274 275 unlink("$TmpDir/test.c"); 276 unlink($EX); 277 } 278 279 if(not $Arch) { 280 exitStatus("Error", "can't check ARCH type"); 281 } 282 283 return ($Cache{"getArch_GCC"}{$LVer} = $Arch); 284} 285 286return 1; 287