blob: 22d76dcbcdc231f4652702ac4c7aca054051bef5 [file] [log] [blame]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001import os, struct, mmap
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002
3class NotELFFileError(Exception):
4 pass
5
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006class ELFFile:
7 EI_NIDENT = 16
8
9 EI_CLASS = 4
10 EI_DATA = 5
11 EI_VERSION = 6
12 EI_OSABI = 7
13 EI_ABIVERSION = 8
14
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050015 E_MACHINE = 0x12
16
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017 # possible values for EI_CLASS
18 ELFCLASSNONE = 0
19 ELFCLASS32 = 1
20 ELFCLASS64 = 2
21
22 # possible value for EI_VERSION
23 EV_CURRENT = 1
24
25 # possible values for EI_DATA
Brad Bishop37a0e4d2017-12-04 01:01:44 -050026 EI_DATA_NONE = 0
27 EI_DATA_LSB = 1
28 EI_DATA_MSB = 2
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050030 PT_INTERP = 3
31
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032 def my_assert(self, expectation, result):
33 if not expectation == result:
34 #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050035 raise NotELFFileError("%s is not an ELF" % self.name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036
Brad Bishop37a0e4d2017-12-04 01:01:44 -050037 def __init__(self, name):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050038 self.name = name
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039 self.objdump_output = {}
40
Brad Bishop37a0e4d2017-12-04 01:01:44 -050041 # Context Manager functions to close the mmap explicitly
42 def __enter__(self):
43 return self
44
45 def __exit__(self, exc_type, exc_value, traceback):
46 self.data.close()
47
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048 def open(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060049 with open(self.name, "rb") as f:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050050 try:
51 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
52 except ValueError:
53 # This means the file is empty
54 raise NotELFFileError("%s is empty" % self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050055
Brad Bishop37a0e4d2017-12-04 01:01:44 -050056 # Check the file has the minimum number of ELF table entries
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050057 if len(self.data) < ELFFile.EI_NIDENT + 4:
58 raise NotELFFileError("%s is not an ELF" % self.name)
59
Brad Bishop37a0e4d2017-12-04 01:01:44 -050060 # ELF header
Patrick Williamsc0f7c042017-02-23 20:41:17 -060061 self.my_assert(self.data[0], 0x7f)
62 self.my_assert(self.data[1], ord('E'))
63 self.my_assert(self.data[2], ord('L'))
64 self.my_assert(self.data[3], ord('F'))
Brad Bishop37a0e4d2017-12-04 01:01:44 -050065 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
66 self.bits = 32
67 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
68 self.bits = 64
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069 else:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050070 # Not 32-bit or 64.. lets assert
71 raise NotELFFileError("ELF but not 32 or 64 bit.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060072 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073
Brad Bishop37a0e4d2017-12-04 01:01:44 -050074 self.endian = self.data[ELFFile.EI_DATA]
75 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
76 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
78 def osAbi(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 return self.data[ELFFile.EI_OSABI]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080
81 def abiVersion(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 return self.data[ELFFile.EI_ABIVERSION]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 def abiSize(self):
85 return self.bits
86
87 def isLittleEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050088 return self.endian == ELFFile.EI_DATA_LSB
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050090 def isBigEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050091 return self.endian == ELFFile.EI_DATA_MSB
92
93 def getStructEndian(self):
94 return {ELFFile.EI_DATA_LSB: "<",
95 ELFFile.EI_DATA_MSB: ">"}[self.endian]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050096
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050097 def getShort(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050098 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050099
100 def getWord(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500101 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500102
103 def isDynamic(self):
104 """
105 Return True if there is a .interp segment (therefore dynamically
106 linked), otherwise False (statically linked).
107 """
108 offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
109 size = self.getShort(self.bits == 32 and 0x2A or 0x36)
110 count = self.getShort(self.bits == 32 and 0x2C or 0x38)
111
112 for i in range(0, count):
113 p_type = self.getWord(offset + i * size)
114 if p_type == ELFFile.PT_INTERP:
115 return True
116 return False
117
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118 def machine(self):
119 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500120 We know the endian stored in self.endian and we
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121 know the position
122 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500123 return self.getShort(ELFFile.E_MACHINE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124
125 def run_objdump(self, cmd, d):
126 import bb.process
127 import sys
128
129 if cmd in self.objdump_output:
130 return self.objdump_output[cmd]
131
132 objdump = d.getVar('OBJDUMP', True)
133
134 env = os.environ.copy()
135 env["LC_ALL"] = "C"
136 env["PATH"] = d.getVar('PATH', True)
137
138 try:
139 bb.note("%s %s %s" % (objdump, cmd, self.name))
140 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
141 return self.objdump_output[cmd]
142 except Exception as e:
143 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
144 return ""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500145
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146def elf_machine_to_string(machine):
147 """
148 Return the name of a given ELF e_machine field or the hex value as a string
149 if it isn't recognised.
150 """
151 try:
152 return {
153 0x02: "SPARC",
154 0x03: "x86",
155 0x08: "MIPS",
156 0x14: "PowerPC",
157 0x28: "ARM",
158 0x2A: "SuperH",
159 0x32: "IA-64",
160 0x3E: "x86-64",
161 0xB7: "AArch64"
162 }[machine]
163 except:
164 return "Unknown (%s)" % repr(machine)
165
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500166if __name__ == "__main__":
167 import sys
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500168
169 with ELFFile(sys.argv[1]) as elf:
170 elf.open()
171 print(elf.isDynamic())