diff options
author | Ross Burton <ross.burton@intel.com> | 2016-11-07 14:11:01 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-11-15 15:19:53 +0000 |
commit | 025f917cec255e1f65d8b8b6c37a1a315173ea8c (patch) | |
tree | bca367dfa670675fe70318b3dfd958aea0a2682f /meta | |
parent | ef1d874c63ad477af2db27eacf889a8c6eb9b566 (diff) | |
download | poky-025f917cec255e1f65d8b8b6c37a1a315173ea8c.tar.gz |
lib/oe/qa: handle binaries with segments outside the first 4kb
The ELF parser was assuming that the segment tables are in the first 4kb of the
binary. Whilst this generally appears to be the case, there have been instances
where the segment table is elsewhere (offset 2MB, in this sample I have). Solve
this problem by mmap()ing the file instead.
Also clean up the code a little whilst chasing the problem.
(From OE-Core rev: a66660aa5bb709547ce0b65a4563e4217c3c3d9f)
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r-- | meta/lib/oe/qa.py | 82 |
1 files changed, 41 insertions, 41 deletions
diff --git a/meta/lib/oe/qa.py b/meta/lib/oe/qa.py index fbe719d8ec..22d76dcbcd 100644 --- a/meta/lib/oe/qa.py +++ b/meta/lib/oe/qa.py | |||
@@ -1,4 +1,4 @@ | |||
1 | import os, struct | 1 | import os, struct, mmap |
2 | 2 | ||
3 | class NotELFFileError(Exception): | 3 | class NotELFFileError(Exception): |
4 | pass | 4 | pass |
@@ -23,9 +23,9 @@ class ELFFile: | |||
23 | EV_CURRENT = 1 | 23 | EV_CURRENT = 1 |
24 | 24 | ||
25 | # possible values for EI_DATA | 25 | # possible values for EI_DATA |
26 | ELFDATANONE = 0 | 26 | EI_DATA_NONE = 0 |
27 | ELFDATA2LSB = 1 | 27 | EI_DATA_LSB = 1 |
28 | ELFDATA2MSB = 2 | 28 | EI_DATA_MSB = 2 |
29 | 29 | ||
30 | PT_INTERP = 3 | 30 | PT_INTERP = 3 |
31 | 31 | ||
@@ -34,51 +34,46 @@ class ELFFile: | |||
34 | #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name) | 34 | #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name) |
35 | raise NotELFFileError("%s is not an ELF" % self.name) | 35 | raise NotELFFileError("%s is not an ELF" % self.name) |
36 | 36 | ||
37 | def __init__(self, name, bits = 0): | 37 | def __init__(self, name): |
38 | self.name = name | 38 | self.name = name |
39 | self.bits = bits | ||
40 | self.objdump_output = {} | 39 | self.objdump_output = {} |
41 | 40 | ||
42 | def open(self): | 41 | # Context Manager functions to close the mmap explicitly |
43 | if not os.path.isfile(self.name): | 42 | def __enter__(self): |
44 | raise NotELFFileError("%s is not a normal file" % self.name) | 43 | return self |
44 | |||
45 | def __exit__(self, exc_type, exc_value, traceback): | ||
46 | self.data.close() | ||
45 | 47 | ||
48 | def open(self): | ||
46 | with open(self.name, "rb") as f: | 49 | with open(self.name, "rb") as f: |
47 | # Read 4k which should cover most of the headers we're after | 50 | try: |
48 | self.data = f.read(4096) | 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) | ||
49 | 55 | ||
56 | # Check the file has the minimum number of ELF table entries | ||
50 | if len(self.data) < ELFFile.EI_NIDENT + 4: | 57 | if len(self.data) < ELFFile.EI_NIDENT + 4: |
51 | raise NotELFFileError("%s is not an ELF" % self.name) | 58 | raise NotELFFileError("%s is not an ELF" % self.name) |
52 | 59 | ||
60 | # ELF header | ||
53 | self.my_assert(self.data[0], 0x7f) | 61 | self.my_assert(self.data[0], 0x7f) |
54 | self.my_assert(self.data[1], ord('E')) | 62 | self.my_assert(self.data[1], ord('E')) |
55 | self.my_assert(self.data[2], ord('L')) | 63 | self.my_assert(self.data[2], ord('L')) |
56 | self.my_assert(self.data[3], ord('F')) | 64 | self.my_assert(self.data[3], ord('F')) |
57 | if self.bits == 0: | 65 | if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32: |
58 | if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32: | 66 | self.bits = 32 |
59 | self.bits = 32 | 67 | elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64: |
60 | elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64: | 68 | self.bits = 64 |
61 | self.bits = 64 | ||
62 | else: | ||
63 | # Not 32-bit or 64.. lets assert | ||
64 | raise NotELFFileError("ELF but not 32 or 64 bit.") | ||
65 | elif self.bits == 32: | ||
66 | self.my_assert(self.data[ELFFile.EI_CLASS], ELFFile.ELFCLASS32) | ||
67 | elif self.bits == 64: | ||
68 | self.my_assert(self.data[ELFFile.EI_CLASS], ELFFile.ELFCLASS64) | ||
69 | else: | 69 | else: |
70 | raise NotELFFileError("Must specify unknown, 32 or 64 bit size.") | 70 | # Not 32-bit or 64.. lets assert |
71 | raise NotELFFileError("ELF but not 32 or 64 bit.") | ||
71 | self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT) | 72 | self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT) |
72 | 73 | ||
73 | self.sex = self.data[ELFFile.EI_DATA] | 74 | self.endian = self.data[ELFFile.EI_DATA] |
74 | if self.sex == ELFFile.ELFDATANONE: | 75 | if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB): |
75 | raise NotELFFileError("self.sex == ELFDATANONE") | 76 | raise NotELFFileError("Unexpected EI_DATA %x" % self.endian) |
76 | elif self.sex == ELFFile.ELFDATA2LSB: | ||
77 | self.sex = "<" | ||
78 | elif self.sex == ELFFile.ELFDATA2MSB: | ||
79 | self.sex = ">" | ||
80 | else: | ||
81 | raise NotELFFileError("Unknown self.sex") | ||
82 | 77 | ||
83 | def osAbi(self): | 78 | def osAbi(self): |
84 | return self.data[ELFFile.EI_OSABI] | 79 | return self.data[ELFFile.EI_OSABI] |
@@ -90,16 +85,20 @@ class ELFFile: | |||
90 | return self.bits | 85 | return self.bits |
91 | 86 | ||
92 | def isLittleEndian(self): | 87 | def isLittleEndian(self): |
93 | return self.sex == "<" | 88 | return self.endian == ELFFile.EI_DATA_LSB |
94 | 89 | ||
95 | def isBigEndian(self): | 90 | def isBigEndian(self): |
96 | return self.sex == ">" | 91 | 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] | ||
97 | 96 | ||
98 | def getShort(self, offset): | 97 | def getShort(self, offset): |
99 | return struct.unpack_from(self.sex+"H", self.data, offset)[0] | 98 | return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0] |
100 | 99 | ||
101 | def getWord(self, offset): | 100 | def getWord(self, offset): |
102 | return struct.unpack_from(self.sex+"i", self.data, offset)[0] | 101 | return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0] |
103 | 102 | ||
104 | def isDynamic(self): | 103 | def isDynamic(self): |
105 | """ | 104 | """ |
@@ -118,7 +117,7 @@ class ELFFile: | |||
118 | 117 | ||
119 | def machine(self): | 118 | def machine(self): |
120 | """ | 119 | """ |
121 | We know the sex stored in self.sex and we | 120 | We know the endian stored in self.endian and we |
122 | know the position | 121 | know the position |
123 | """ | 122 | """ |
124 | return self.getShort(ELFFile.E_MACHINE) | 123 | return self.getShort(ELFFile.E_MACHINE) |
@@ -166,6 +165,7 @@ def elf_machine_to_string(machine): | |||
166 | 165 | ||
167 | if __name__ == "__main__": | 166 | if __name__ == "__main__": |
168 | import sys | 167 | import sys |
169 | elf = ELFFile(sys.argv[1]) | 168 | |
170 | elf.open() | 169 | with ELFFile(sys.argv[1]) as elf: |
171 | print(elf.isDynamic()) | 170 | elf.open() |
171 | print(elf.isDynamic()) | ||