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
24require "config"
25require "ast"
26
27def to32Bit(value)
28    if value > 0x7fffffff
29        value -= 1 << 32
30    end
31    value
32end
33
34OFFSET_HEADER_MAGIC_NUMBERS = [ to32Bit(0x9e43fd66), to32Bit(0x4379bfba) ]
35OFFSET_MAGIC_NUMBERS = [ to32Bit(0xec577ac7), to32Bit(0x0ff5e755) ]
36
37#
38# MissingMagicValuesException
39#
40# Thrown when magic values are missing from the binary.
41#
42
43class MissingMagicValuesException < Exception
44end
45
46#
47# offsetsList(ast)
48# sizesList(ast)
49#
50# Returns a list of offsets and sizes used by the AST.
51#
52
53def offsetsList(ast)
54    ast.filter(StructOffset).uniq.sort
55end
56
57def sizesList(ast)
58    ast.filter(Sizeof).uniq.sort
59end
60
61#
62# offsetsAndConfigurationIndex(ast, file) ->
63#     [[offsets, index], ...]
64#
65# Parses the offsets from a file and returns a list of offsets and the
66# index of the configuration that is valid in this build target.
67#
68
69def offsetsAndConfigurationIndex(file)
70    endiannessMarkerBytes = nil
71    result = {}
72    
73    def readInt(endianness, bytes)
74        if endianness == :little
75            # Little endian
76            (bytes[0] << 0 |
77             bytes[1] << 8 |
78             bytes[2] << 16 |
79             bytes[3] << 24)
80        else
81            # Big endian
82            (bytes[0] << 24 |
83             bytes[1] << 16 |
84             bytes[2] << 8 |
85             bytes[3] << 0)
86        end
87    end
88    
89    def prepareMagic(endianness, numbers)
90        magicBytes = []
91        numbers.each {
92            | number |
93            currentBytes = []
94            4.times {
95                currentBytes << (number & 0xff)
96                number >>= 8
97            }
98            if endianness == :big
99                currentBytes.reverse!
100            end
101            magicBytes += currentBytes
102        }
103        magicBytes
104    end
105    
106    fileBytes = []
107    
108    File.open(file, "rb") {
109        | inp |
110        loop {
111            byte = inp.getbyte
112            break unless byte
113            fileBytes << byte
114        }
115    }
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    [:little, :big].each {
142        | endianness |
143        headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS)
144        magicBytes = prepareMagic(endianness, OFFSET_MAGIC_NUMBERS)
145        
146        bigArray = sliceByteArrays(fileBytes, headerMagicBytes)
147        unless bigArray.size <= 1
148            bigArray[1..-1].each {
149                | configArray |
150                array = sliceByteArrays(configArray, magicBytes)
151                index = readInt(endianness, array[1])
152                offsets = []
153                array[2..-1].each {
154                    | data |
155                    offsets << readInt(endianness, data)
156                }
157                result[index] = offsets
158            }
159        end
160    }
161    
162    raise MissingMagicValuesException unless result.length >= 1
163    
164    # result is {index1=>offsets1, index2=>offsets2} but we want to return
165    # [[offsets1, index1], [offsets2, index2]].
166    return result.map {
167        | pair |
168        pair.reverse
169    }
170end
171
172#
173# buildOffsetsMap(ast, offsetsList) -> [offsets, sizes]
174#
175# Builds a mapping between StructOffset nodes and their values.
176#
177
178def buildOffsetsMap(ast, offsetsList)
179    offsetsMap = {}
180    sizesMap = {}
181    astOffsetsList = offsetsList(ast)
182    astSizesList = sizesList(ast)
183    raise unless astOffsetsList.size + astSizesList.size == offsetsList.size
184    offsetsList(ast).each_with_index {
185        | structOffset, index |
186        offsetsMap[structOffset] = offsetsList.shift
187    }
188    sizesList(ast).each_with_index {
189        | sizeof, index |
190        sizesMap[sizeof] = offsetsList.shift
191    }
192    [offsetsMap, sizesMap]
193end
194
195