| # Copyright (C) 2011 Apple Inc. All rights reserved. | 
 | # | 
 | # Redistribution and use in source and binary forms, with or without | 
 | # modification, are permitted provided that the following conditions | 
 | # are met: | 
 | # 1. Redistributions of source code must retain the above copyright | 
 | #    notice, this list of conditions and the following disclaimer. | 
 | # 2. Redistributions in binary form must reproduce the above copyright | 
 | #    notice, this list of conditions and the following disclaimer in the | 
 | #    documentation and/or other materials provided with the distribution. | 
 | # | 
 | # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 
 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | 
 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 
 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
 | # THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | require "config" | 
 | require "ast" | 
 |  | 
 | def to32Bit(value) | 
 |     if value > 0x7fffffff | 
 |         value -= 1 << 32 | 
 |     end | 
 |     value | 
 | end | 
 |  | 
 | OFFSET_HEADER_MAGIC_NUMBERS = [ to32Bit(0x9e43fd66), to32Bit(0x4379bfba) ] | 
 | OFFSET_MAGIC_NUMBERS = [ to32Bit(0xec577ac7), to32Bit(0x0ff5e755) ] | 
 |  | 
 | # | 
 | # MissingMagicValuesException | 
 | # | 
 | # Thrown when magic values are missing from the binary. | 
 | # | 
 |  | 
 | class MissingMagicValuesException < Exception | 
 | end | 
 |  | 
 | # | 
 | # offsetsList(ast) | 
 | # sizesList(ast) | 
 | # | 
 | # Returns a list of offsets and sizes used by the AST. | 
 | # | 
 |  | 
 | def offsetsList(ast) | 
 |     ast.filter(StructOffset).uniq.sort | 
 | end | 
 |  | 
 | def sizesList(ast) | 
 |     ast.filter(Sizeof).uniq.sort | 
 | end | 
 |  | 
 | # | 
 | # offsetsAndConfigurationIndex(ast, file) -> | 
 | #     [[offsets, index], ...] | 
 | # | 
 | # Parses the offsets from a file and returns a list of offsets and the | 
 | # index of the configuration that is valid in this build target. | 
 | # | 
 |  | 
 | def offsetsAndConfigurationIndex(file) | 
 |     endiannessMarkerBytes = nil | 
 |     result = {} | 
 |      | 
 |     def readInt(endianness, bytes) | 
 |         if endianness == :little | 
 |             # Little endian | 
 |             (bytes[0] << 0 | | 
 |              bytes[1] << 8 | | 
 |              bytes[2] << 16 | | 
 |              bytes[3] << 24) | 
 |         else | 
 |             # Big endian | 
 |             (bytes[0] << 24 | | 
 |              bytes[1] << 16 | | 
 |              bytes[2] << 8 | | 
 |              bytes[3] << 0) | 
 |         end | 
 |     end | 
 |      | 
 |     def prepareMagic(endianness, numbers) | 
 |         magicBytes = [] | 
 |         numbers.each { | 
 |             | number | | 
 |             currentBytes = [] | 
 |             4.times { | 
 |                 currentBytes << (number & 0xff) | 
 |                 number >>= 8 | 
 |             } | 
 |             if endianness == :big | 
 |                 currentBytes.reverse! | 
 |             end | 
 |             magicBytes += currentBytes | 
 |         } | 
 |         magicBytes | 
 |     end | 
 |      | 
 |     fileBytes = [] | 
 |      | 
 |     File.open(file, "rb") { | 
 |         | inp | | 
 |         loop { | 
 |             byte = inp.getbyte | 
 |             break unless byte | 
 |             fileBytes << byte | 
 |         } | 
 |     } | 
 |      | 
 |     def sliceByteArrays(byteArray, pattern) | 
 |         result = [] | 
 |         lastSlicePoint = 0 | 
 |         (byteArray.length - pattern.length + 1).times { | 
 |             | index | | 
 |             foundOne = true | 
 |             pattern.length.times { | 
 |                 | subIndex | | 
 |                 if byteArray[index + subIndex] != pattern[subIndex] | 
 |                     foundOne = false | 
 |                     break | 
 |                 end | 
 |             } | 
 |             if foundOne | 
 |                 result << byteArray[lastSlicePoint...index] | 
 |                 lastSlicePoint = index + pattern.length | 
 |             end | 
 |         } | 
 |          | 
 |         result << byteArray[lastSlicePoint...(byteArray.length)] | 
 |          | 
 |         result | 
 |     end | 
 |      | 
 |     [:little, :big].each { | 
 |         | endianness | | 
 |         headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS) | 
 |         magicBytes = prepareMagic(endianness, OFFSET_MAGIC_NUMBERS) | 
 |          | 
 |         bigArray = sliceByteArrays(fileBytes, headerMagicBytes) | 
 |         unless bigArray.size <= 1 | 
 |             bigArray[1..-1].each { | 
 |                 | configArray | | 
 |                 array = sliceByteArrays(configArray, magicBytes) | 
 |                 index = readInt(endianness, array[1]) | 
 |                 offsets = [] | 
 |                 array[2..-1].each { | 
 |                     | data | | 
 |                     offsets << readInt(endianness, data) | 
 |                 } | 
 |                 result[index] = offsets | 
 |             } | 
 |         end | 
 |     } | 
 |      | 
 |     raise MissingMagicValuesException unless result.length >= 1 | 
 |      | 
 |     # result is {index1=>offsets1, index2=>offsets2} but we want to return | 
 |     # [[offsets1, index1], [offsets2, index2]]. | 
 |     return result.map { | 
 |         | pair | | 
 |         pair.reverse | 
 |     } | 
 | end | 
 |  | 
 | # | 
 | # buildOffsetsMap(ast, offsetsList) -> [offsets, sizes] | 
 | # | 
 | # Builds a mapping between StructOffset nodes and their values. | 
 | # | 
 |  | 
 | def buildOffsetsMap(ast, offsetsList) | 
 |     offsetsMap = {} | 
 |     sizesMap = {} | 
 |     astOffsetsList = offsetsList(ast) | 
 |     astSizesList = sizesList(ast) | 
 |     raise unless astOffsetsList.size + astSizesList.size == offsetsList.size | 
 |     offsetsList(ast).each_with_index { | 
 |         | structOffset, index | | 
 |         offsetsMap[structOffset] = offsetsList.shift | 
 |     } | 
 |     sizesList(ast).each_with_index { | 
 |         | sizeof, index | | 
 |         sizesMap[sizeof] = offsetsList.shift | 
 |     } | 
 |     [offsetsMap, sizesMap] | 
 | end | 
 |  |