8d6383b0673754fcc56a888d7b4942b85c5bea45
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / offsets.rb
1 # Copyright (C) 2011 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 #    notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 #    notice, this list of conditions and the following disclaimer in the
10 #    documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22 # THE POSSIBILITY OF SUCH DAMAGE.
23
24 require "config"
25 require "ast"
26
27 OFFSET_HEADER_MAGIC_NUMBERS = [ 0x2e43fd66, 0x4379bfba ]
28 OFFSET_MAGIC_NUMBERS = [ 0x5c577ac7, 0x0ff5e755 ]
29
30 #
31 # MissingMagicValuesException
32 #
33 # Thrown when magic values are missing from the binary.
34 #
35
36 class MissingMagicValuesException < Exception
37 end
38
39 #
40 # offsetsList(ast)
41 # sizesList(ast)
42 # constsLists(ast)
43 #
44 # Returns a list of offsets, sizeofs, and consts used by the AST.
45 #
46
47 def offsetsList(ast)
48     ast.filter(StructOffset).uniq.sort
49 end
50
51 def sizesList(ast)
52     ast.filter(Sizeof).uniq.sort
53 end
54
55 def constsList(ast)
56     ast.filter(ConstExpr).uniq.sort
57 end
58
59 def readInt(endianness, bytes)
60     if endianness == :little
61         # Little endian
62         number = (bytes[0] << 0  |
63                   bytes[1] << 8  |
64                   bytes[2] << 16 |
65                   bytes[3] << 24 |
66                   bytes[4] << 32 |
67                   bytes[5] << 40 |
68                   bytes[6] << 48 |
69                   bytes[7] << 56)
70     else
71         # Big endian
72         number = (bytes[0] << 56 |
73                   bytes[1] << 48 |
74                   bytes[2] << 40 |
75                   bytes[3] << 32 |
76                   bytes[4] << 24 |
77                   bytes[5] << 16 |
78                   bytes[6] << 8  |
79                   bytes[7] << 0)
80     end
81     if number > 0x7fffffff_ffffffff
82         number -= 1 << 64
83     end
84     number
85 end
86
87 def prepareMagic(endianness, numbers)
88     magicBytes = []
89     numbers.each {
90         | number |
91         currentBytes = []
92         8.times {
93             currentBytes << (number & 0xff)
94             number >>= 8
95         }
96         if endianness == :big
97             currentBytes.reverse!
98         end
99         magicBytes += currentBytes
100     }
101     magicBytes
102 end
103
104 def fileBytes(file)
105     fileBytes = []
106     File.open(file, "rb") {
107         | inp |
108         loop {
109             byte = inp.getbyte
110             break unless byte
111             fileBytes << byte
112         }
113     }
114     fileBytes
115 end
116
117 def sliceByteArrays(byteArray, pattern)
118     result = []
119     lastSlicePoint = 0
120     (byteArray.length - pattern.length + 1).times {
121         | index |
122         foundOne = true
123         pattern.length.times {
124             | subIndex |
125             if byteArray[index + subIndex] != pattern[subIndex]
126                 foundOne = false
127                 break
128             end
129         }
130         if foundOne
131             result << byteArray[lastSlicePoint...index]
132             lastSlicePoint = index + pattern.length
133         end
134     }
135
136     result << byteArray[lastSlicePoint...(byteArray.length)]
137
138     result
139 end
140
141 #
142 # offsetsAndConfigurationIndex(ast, file) ->
143 #     [[offsets, index], ...]
144 #
145 # Parses the offsets from a file and returns a list of offsets and the
146 # index of the configuration that is valid in this build target.
147 #
148
149 def offsetsAndConfigurationIndex(file)
150     fileBytes = fileBytes(file)
151     result = {}
152
153     [:little, :big].each {
154         | endianness |
155         headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS)
156         magicBytes = prepareMagic(endianness, OFFSET_MAGIC_NUMBERS)
157
158         bigArray = sliceByteArrays(fileBytes, headerMagicBytes)
159         unless bigArray.size <= 1
160             bigArray[1..-1].each {
161                 | configArray |
162                 array = sliceByteArrays(configArray, magicBytes)
163                 index = readInt(endianness, array[1])
164                 offsets = []
165                 array[2..-1].each {
166                     | data |
167                     offsets << readInt(endianness, data)
168                 }
169                 result[index] = offsets
170             }
171         end
172     }
173
174     raise MissingMagicValuesException unless result.length >= 1
175
176     # result is {index1=>offsets1, index2=>offsets2} but we want to return
177     # [[offsets1, index1], [offsets2, index2]].
178     return result.map {
179         | pair |
180         pair.reverse
181     }
182 end
183
184 #
185 # configurationIndices(ast, file) ->
186 #     [[offsets, index], ...]
187 #
188 # Parses the configurations from a file and returns a list of the indices of
189 # the configurations that are valid in this build target.
190 #
191
192 def configurationIndices(file)
193     fileBytes = fileBytes(file)
194     result = []
195
196     [:little, :big].each {
197         | endianness |
198         headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS)
199
200         bigArray = sliceByteArrays(fileBytes, headerMagicBytes)
201         unless bigArray.size <= 1
202             bigArray[1..-1].each {
203                 | configArray |
204                 result << readInt(endianness, configArray)
205             }
206         end
207     }
208
209     raise MissingMagicValuesException unless result.length >= 1
210
211     return result
212 end
213
214 #
215 # buildOffsetsMap(ast, extractedConstants) -> map
216 #
217 # Builds a mapping between StructOffset, Sizeof, and ConstExpr nodes and their values.
218 #
219
220 def buildOffsetsMap(ast, extractedConstants)
221     map = {}
222     astOffsetsList = offsetsList(ast)
223     astSizesList = sizesList(ast)
224     astConstsList = constsList(ast)
225
226     raise unless astOffsetsList.size + astSizesList.size + astConstsList.size == extractedConstants.size
227     astOffsetsList.each_with_index {
228         | structOffset, index |
229         map[structOffset] = extractedConstants.shift
230     }
231     astSizesList.each_with_index {
232         | sizeof, index |
233         map[sizeof] = extractedConstants.shift
234     }
235     astConstsList.each_with_index {
236         | const, index |
237         map[const] = extractedConstants.shift
238     }
239     map
240 end
241