| # 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" |
| |
| OFFSET_HEADER_MAGIC_NUMBERS = [ 0x2e43fd66, 0x4379bfba ] |
| OFFSET_MAGIC_NUMBERS = [ 0x5c577ac7, 0x0ff5e755 ] |
| |
| # |
| # MissingMagicValuesException |
| # |
| # Thrown when magic values are missing from the binary. |
| # |
| |
| class MissingMagicValuesException < Exception |
| end |
| |
| # |
| # offsetsList(ast) |
| # sizesList(ast) |
| # constsLists(ast) |
| # |
| # Returns a list of offsets, sizeofs, and consts used by the AST. |
| # |
| |
| def offsetsList(ast) |
| ast.filter(StructOffset).uniq.sort |
| end |
| |
| def sizesList(ast) |
| ast.filter(Sizeof).uniq.sort |
| end |
| |
| def constsList(ast) |
| ast.filter(ConstExpr).uniq.sort |
| end |
| |
| def readInt(endianness, bytes) |
| if endianness == :little |
| # Little endian |
| number = (bytes[0] << 0 | |
| bytes[1] << 8 | |
| bytes[2] << 16 | |
| bytes[3] << 24 | |
| bytes[4] << 32 | |
| bytes[5] << 40 | |
| bytes[6] << 48 | |
| bytes[7] << 56) |
| else |
| # Big endian |
| number = (bytes[0] << 56 | |
| bytes[1] << 48 | |
| bytes[2] << 40 | |
| bytes[3] << 32 | |
| bytes[4] << 24 | |
| bytes[5] << 16 | |
| bytes[6] << 8 | |
| bytes[7] << 0) |
| end |
| if number > 0x7fffffff_ffffffff |
| number -= 1 << 64 |
| end |
| number |
| end |
| |
| def prepareMagic(endianness, numbers) |
| magicBytes = [] |
| numbers.each { |
| | number | |
| currentBytes = [] |
| 8.times { |
| currentBytes << (number & 0xff) |
| number >>= 8 |
| } |
| if endianness == :big |
| currentBytes.reverse! |
| end |
| magicBytes += currentBytes |
| } |
| magicBytes |
| end |
| |
| def fileBytes(file) |
| fileBytes = [] |
| File.open(file, "rb") { |
| | inp | |
| loop { |
| byte = inp.getbyte |
| break unless byte |
| fileBytes << byte |
| } |
| } |
| fileBytes |
| end |
| |
| 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 |
| |
| # |
| # offsetsAndConfigurationIndex(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) |
| fileBytes = fileBytes(file) |
| result = {} |
| |
| [: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 |
| |
| # |
| # offsetsAndConfigurationIndex(file) -> |
| # [[offsets, index], ...] |
| # |
| # Parses the offsets from a file and all its variants and returns a list of |
| # offsets and the index of the configuration that is valid in this build target. |
| # |
| |
| def offsetsAndConfigurationIndexForVariants(file, variants) |
| results = [] |
| variants.each { |
| | current_variant | |
| suffix = "" |
| unless current_variant == "normal" |
| suffix = "_" + current_variant |
| end |
| results << offsetsAndConfigurationIndex(file + suffix) |
| } |
| return results.flatten(1) |
| end |
| |
| # |
| # configurationIndices(file) -> |
| # [[offsets, index], ...] |
| # |
| # Parses the configurations from a file and returns a list of the indices of |
| # the configurations that are valid in this build target. |
| # |
| |
| def configurationIndices(file) |
| fileBytes = fileBytes(file) |
| result = [] |
| |
| [:little, :big].each { |
| | endianness | |
| headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS) |
| |
| bigArray = sliceByteArrays(fileBytes, headerMagicBytes) |
| unless bigArray.size <= 1 |
| bigArray[1..-1].each { |
| | configArray | |
| result << readInt(endianness, configArray) |
| } |
| end |
| } |
| |
| raise MissingMagicValuesException unless result.length >= 1 |
| |
| return result |
| end |
| |
| # |
| # configurationIndicesForVariants(file, variants) -> |
| # [[offsets, index], ...] |
| # |
| # Parses the configurations from a file and all its variants and returns a list |
| # of the indices of the configurations that are valid in this build target. |
| # |
| |
| def configurationIndicesForVariants(file, variants) |
| results = [] |
| variants.each { |
| | current_variant | |
| suffix = "" |
| unless current_variant == "normal" |
| suffix = "_" + current_variant |
| end |
| results << configurationIndices(file + suffix) |
| } |
| return results.flatten(1) |
| end |
| |
| # |
| # buildOffsetsMap(ast, extractedConstants) -> map |
| # |
| # Builds a mapping between StructOffset, Sizeof, and ConstExpr nodes and their values. |
| # |
| |
| def buildOffsetsMap(ast, extractedConstants) |
| map = {} |
| astOffsetsList = offsetsList(ast) |
| astSizesList = sizesList(ast) |
| astConstsList = constsList(ast) |
| |
| raise unless astOffsetsList.size + astSizesList.size + astConstsList.size == extractedConstants.size |
| astOffsetsList.each_with_index { |
| | structOffset, index | |
| map[structOffset] = extractedConstants.shift |
| } |
| astSizesList.each_with_index { |
| | sizeof, index | |
| map[sizeof] = extractedConstants.shift |
| } |
| astConstsList.each_with_index { |
| | const, index | |
| map[const] = extractedConstants.shift |
| } |
| map |
| end |
| |