From 450931cda8c545f1182c111e1ee5ca751a0c8042 Mon Sep 17 00:00:00 2001 From: Nick Lambourne Date: Sun, 14 Apr 2024 17:37:57 -0700 Subject: [PATCH] Add in new APIs to remove and list assets in a binary --- lib/moduleEncoding.js | 208 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/lib/moduleEncoding.js b/lib/moduleEncoding.js index ad53890..4e9ae41 100644 --- a/lib/moduleEncoding.js +++ b/lib/moduleEncoding.js @@ -787,6 +787,210 @@ async function addModuleExtensions({ module, exts = [], replaceWithSameType = fa return buffer; } +/** + * Remove extensions of a particular type. + * + * @param {Buffer} module Module to update + * @param {Array} exts List of extensions to add + * @param {Boolean} replaceWithSameType Append extensions as-is or replace existing extensions of the same type + * @returns {Promise} + */ +async function removeModuleExtensions({ module, exts = [] } = {}) { + if (exts.length === 0) { + throw new RangeError('Empty extension list'); + } + + const parser = new HalModuleParser(); + const { prefixInfo: prefix } = await parser.parsePrefix({ fileBuffer: module }); + const { suffixInfo: suffix } = await parser.parseSuffix({ fileBuffer: module }); + prefix.moduleStartAddy = sanitizeAddress(prefix.moduleStartAddy); + prefix.moduleEndAddy = sanitizeAddress(prefix.moduleEndAddy); + const flags = prefix.moduleFlags; + if (flags & ModuleFlags.COMPRESSED) { + throw new RangeError(`Can't add extensions to a compressed module`); + } + if (flags & ModuleFlags.COMBINED) { + throw new RangeError(`Can\'t add extensions to a combined module`); + } + const suffixSize = suffix.suffixSize; + if (suffixSize < MIN_MODULE_SUFFIX_SIZE || suffixSize > MAX_MODULE_SUFFIX_SIZE) { + throw new RangeError('Invalid suffix size'); + } + let dataSize = prefix.moduleEndAddy - prefix.moduleStartAddy + 4 /* CRC-32 */; + const prefixOffs = prefix.prefixOffset || 0; + if (module.length < prefixOffs + MODULE_PREFIX_SIZE + suffixSize + 4 || module.length !== dataSize) { + throw new RangeError('Invalid size of the module data'); + } + + const moduleInfo = firmwareModuleInfoForPlatformAndFunction(prefix.platformID, prefix.moduleFunction, prefix.moduleIndex); + let extensionsInPrefix = false; + if ((prefix.moduleFlags & ModuleInfo.Flags.PREFIX_EXTENSIONS) || (moduleInfo && moduleInfo.growsLeft)) { + extensionsInPrefix = true; + } + + let extensions = []; + if (extensionsInPrefix) { + extensions = prefix.extensions; + } else { + extensions = suffix.extensions; + } + if (!extensions) { + extensions = []; + } + + extensions = extensions.filter((val) => { + for (let ext of exts) { + if (val.type === ext) { + return false; + } + } + return true; + }); + + let buffer = null; + + if (extensionsInPrefix) {; + prefix.extensions = extensions; + } else { + suffix.extensions = extensions; + } + + // Always add END extension: it is mandatory to be present in module prefix extensions + // and due to the fact that product data for some of the platforms may not use an extension and just be + // raw data, to indicate end we will also use END extension here. + if (extensions.length > 0 && extensions[extensions.length - 1].type !== ModuleInfo.ModuleInfoExtension.END && + extensions[extensions.length - 1].type !== ModuleInfo.ModuleInfoExtension.PRODUCT_DATA) { + extensions.push({ + type: ModuleInfo.ModuleInfoExtension.END + }); + } + + if (extensionsInPrefix && extensions.length > 0) { + if (prefixOffs > 0) { + throw new RangeError('Extensions in prefix are not supported for modules with non-zero prefix offset'); + } + prefix.moduleFlags |= ModuleInfo.Flags.PREFIX_EXTENSIONS; + prefix.extensions = extensions; + const originalPrefixSize = prefix.prefixSize; + delete extensions[extensions.length - 1].data; + delete extensions[extensions.length - 1].length; + prefix.prefixSize = calculateModulePrefixSize(prefix); + + let newSize = module.length + (prefix.prefixSize - originalPrefixSize); + let newStartAddr = prefix.moduleEndAddy - (newSize - 4); + let alignment = 0; + if (newStartAddr % DEFAULT_START_ALIGNMENT !== 0) { + alignment = (newStartAddr % DEFAULT_START_ALIGNMENT); + newSize += alignment; + } + + extensions[extensions.length - 1].padding = alignment; + + prefix.prefixSize += alignment; + + console.log(prefix); + + buffer = Buffer.alloc(newSize); + module.copy(buffer, prefix.prefixSize, originalPrefixSize /* source offset */); + prefix.moduleStartAddy = prefix.moduleEndAddy - (buffer.length - 4); + + if (suffix.extensions) { + for (let ext of suffix.extensions) { + if (ext.type === ModuleInfo.ModuleInfoExtension.DYNAMIC_LOCATION) { + // This has to be updated for certain platforms if start address is moved + delete ext.data; + ext.moduleStartAddress = prefix.moduleStartAddy; + } + } + } + } else if (extensions.length > 0) { + // In suffix + suffix.extensions = extensions; + const originalSuffixSize = suffix.suffixSize; + suffix.suffixSize = calculateModuleSuffixSize(suffix); + + buffer = Buffer.alloc(module.length + (suffix.suffixSize - originalSuffixSize)); + module.copy(buffer, 0, 0, module.length - originalSuffixSize - 4 /* CRC-32 */); + prefix.moduleEndAddy = prefix.moduleStartAddy + buffer.length - 4; + } + + if (moduleInfo && moduleInfo.maxSize) { + if (buffer.length > moduleInfo.maxSize) { + throw new PlatformLimitError({}, 'Resulting module exceeds platform size limits'); + } + } + + updateModulePrefix(buffer, prefix); + updateModuleSuffix(buffer, suffix); + updateModuleSha256(buffer); + updateModuleCrc32(buffer); + + return buffer; +} + +/** + * list extensions of a particular type. + * + * @param {Buffer} module Module to update + * @param {Array} exts List of extensions to add + * @returns {Promise<[]>} + */ +async function listModuleExtensions({ module, exts = [] } = {}) { + if (exts.length === 0) { + throw new RangeError('Empty extension list'); + } + + const parser = new HalModuleParser(); + const { prefixInfo: prefix } = await parser.parsePrefix({ fileBuffer: module }); + const { suffixInfo: suffix } = await parser.parseSuffix({ fileBuffer: module }); + prefix.moduleStartAddy = sanitizeAddress(prefix.moduleStartAddy); + prefix.moduleEndAddy = sanitizeAddress(prefix.moduleEndAddy); + const flags = prefix.moduleFlags; + if (flags & ModuleFlags.COMPRESSED) { + throw new RangeError(`Can't add extensions to a compressed module`); + } + if (flags & ModuleFlags.COMBINED) { + throw new RangeError(`Can\'t add extensions to a combined module`); + } + const suffixSize = suffix.suffixSize; + if (suffixSize < MIN_MODULE_SUFFIX_SIZE || suffixSize > MAX_MODULE_SUFFIX_SIZE) { + throw new RangeError('Invalid suffix size'); + } + let dataSize = prefix.moduleEndAddy - prefix.moduleStartAddy + 4 /* CRC-32 */; + const prefixOffs = prefix.prefixOffset || 0; + if (module.length < prefixOffs + MODULE_PREFIX_SIZE + suffixSize + 4 || module.length !== dataSize) { + throw new RangeError('Invalid size of the module data'); + } + + const moduleInfo = firmwareModuleInfoForPlatformAndFunction(prefix.platformID, prefix.moduleFunction, prefix.moduleIndex); + let extensionsInPrefix = false; + if ((prefix.moduleFlags & ModuleInfo.Flags.PREFIX_EXTENSIONS) || (moduleInfo && moduleInfo.growsLeft)) { + extensionsInPrefix = true; + } + + let extensions = []; + if (extensionsInPrefix) { + extensions = prefix.extensions; + } else { + extensions = suffix.extensions; + } + if (!extensions) { + extensions = []; + } + + extensions = extensions.filter((val) => { + for (let ext of exts) { + if (val.type === ext) { + return true; + } + } + return false; + }); + + return extensions; +} + + /** * Update module info to include a set of asset dependencies * @@ -1448,5 +1652,7 @@ module.exports = { AssetLimitError, PlatformLimitError, createProtectedModule, - addModuleExtensions + addModuleExtensions, + removeModuleExtensions, + listModuleExtensions };