aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-devtools/qc-image-unpacker
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-devtools/qc-image-unpacker')
-rw-r--r--recipes-devtools/qc-image-unpacker/qc-image-unpacker/0001-Move-image-format-detection-to-separate-handlers.patch140
-rw-r--r--recipes-devtools/qc-image-unpacker/qc-image-unpacker/0002-Add-support-for-bootldr-images.patch239
-rw-r--r--recipes-devtools/qc-image-unpacker/qc-image-unpacker/0003-Do-not-let-dirname-tamper-with-inputFile.patch30
-rw-r--r--recipes-devtools/qc-image-unpacker/qc-image-unpacker/0004-Fail-if-an-image-can-not-be-opened.patch63
-rw-r--r--recipes-devtools/qc-image-unpacker/qc-image-unpacker_git.bb28
5 files changed, 500 insertions, 0 deletions
diff --git a/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0001-Move-image-format-detection-to-separate-handlers.patch b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0001-Move-image-format-detection-to-separate-handlers.patch
new file mode 100644
index 0000000..468d9db
--- /dev/null
+++ b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0001-Move-image-format-detection-to-separate-handlers.patch
@@ -0,0 +1,140 @@
+From 6b8c83b622984360e4b130e8dd9206c0bca5bffb Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Wed, 10 Nov 2021 01:13:00 +0300
+Subject: [PATCH 1/4] Move image format detection to separate handlers
+
+In preparation to adding another file format, separate header checks
+into foo_image_detect() functions, to keep all the details away from the
+main file.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+---
+ src/meta_image.c | 12 ++++++++++++
+ src/meta_image.h | 1 +
+ src/packed_image.c | 12 ++++++++++++
+ src/packed_image.h | 1 +
+ src/qc_image_unpacker.c | 16 ++++++++--------
+ 5 files changed, 34 insertions(+), 8 deletions(-)
+
+diff --git a/src/meta_image.c b/src/meta_image.c
+index eecf87871a44..300b3fecd796 100644
+--- a/src/meta_image.c
++++ b/src/meta_image.c
+@@ -25,6 +25,18 @@
+ #include "common.h"
+ #include "utils.h"
+
++bool meta_image_detect(u1 *buf, size_t bufSz)
++{
++ meta_header_t *pMetaHeader;
++
++ if (bufSz < sizeof(meta_header_t))
++ return false;
++
++ pMetaHeader = (meta_header_t *)buf;
++
++ return pMetaHeader->magic == META_IMG_MAGIC;
++}
++
+ bool meta_image_extract(u1 *buf, size_t bufSz, char *filePath, char *outputDir) {
+ meta_header_t *pMetaHeader;
+ meta_img_header_entry_t *pImgHeaderEntry;
+diff --git a/src/meta_image.h b/src/meta_image.h
+index 67c1b6d7835f..2bae503f89df 100644
+--- a/src/meta_image.h
++++ b/src/meta_image.h
+@@ -44,6 +44,7 @@ typedef struct meta_img_header_entry {
+ u4 size;
+ } meta_img_header_entry_t;
+
++bool meta_image_detect(u1 *, size_t);
+ bool meta_image_extract(u1 *, size_t, char *, char *);
+
+ #endif
+diff --git a/src/packed_image.c b/src/packed_image.c
+index 4117b14f81a0..585772a2cfac 100644
+--- a/src/packed_image.c
++++ b/src/packed_image.c
+@@ -25,6 +25,18 @@
+ #include "common.h"
+ #include "utils.h"
+
++bool packed_image_detect(u1 *buf, size_t bufSz)
++{
++ packed_header_t *pPackedHeader;
++
++ if (bufSz < sizeof(packed_header_t))
++ return false;
++
++ pPackedHeader = (packed_header_t *)buf;
++
++ return pPackedHeader->magic == PACKED_IMG_MAGIC;
++}
++
+ static bool process_fbpt(u1 *buf, size_t bufSz) {
+ fbpt_header_t *pFBPT = (fbpt_header_t *)buf;
+ if (pFBPT->signature != FBPT_SIGNATURE) {
+diff --git a/src/packed_image.h b/src/packed_image.h
+index f66ce2cf22f2..fba081dc1426 100644
+--- a/src/packed_image.h
++++ b/src/packed_image.h
+@@ -110,6 +110,7 @@ typedef struct fbpt_entry {
+ char pad[2]; // Usually is 0x3030
+ } fbpt_entry_t; // sizeof == 124
+
++bool packed_image_detect(u1 *, size_t);
+ bool packed_image_extract(u1 *, size_t, char *, char *);
+
+ #endif
+diff --git a/src/qc_image_unpacker.c b/src/qc_image_unpacker.c
+index 4fbe8fa6ad6e..44d737803ea0 100644
+--- a/src/qc_image_unpacker.c
++++ b/src/qc_image_unpacker.c
+@@ -122,8 +122,6 @@ int main(int argc, char **argv) {
+ off_t fileSz = 0;
+ int srcfd = -1;
+ u1 *buf = NULL;
+- meta_header_t *pMetaHeader;
+- packed_header_t *pPackedHeader;
+
+ LOGMSG(l_DEBUG, "Processing '%s'", pFiles.files[f]);
+
+@@ -133,27 +131,29 @@ int main(int argc, char **argv) {
+ continue;
+ }
+
+- if ((size_t)fileSz < sizeof(meta_header_t) && (size_t)fileSz < sizeof(packed_header_t)) {
++ /*
++ * Check only if we have something to detect here.
++ * Individual _image_detect() functions check header size.
++ */
++ if ((size_t)fileSz < sizeof(u4)) {
+ LOGMSG(l_ERROR, "Invalid input size - skipping '%s'", pFiles.files[f]);
+ goto next_file;
+ }
+
+- pMetaHeader = (meta_header_t *)buf;
+- pPackedHeader = (packed_header_t *)buf;
+- if (pMetaHeader->magic == META_IMG_MAGIC) {
++ if (meta_image_detect(buf, (size_t)fileSz)) {
+ LOGMSG(l_DEBUG, "Meta image header found");
+ if (!meta_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+ goto next_file;
+ }
+- } else if (pPackedHeader->magic == PACKED_IMG_MAGIC) {
++ } else if (packed_image_detect(buf, (size_t)fileSz)) {
+ LOGMSG(l_DEBUG, "packed image header found");
+ if (!packed_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+ goto next_file;
+ }
+ } else {
+- LOGMSG(l_ERROR, "Invalid magic header 0x%x - skipping '%s'", pMetaHeader->magic,
++ LOGMSG(l_ERROR, "Invalid magic header 0x%x - skipping '%s'", *(u4*)buf,
+ pFiles.files[f]);
+ goto next_file;
+ }
+--
+2.35.1
+
diff --git a/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0002-Add-support-for-bootldr-images.patch b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0002-Add-support-for-bootldr-images.patch
new file mode 100644
index 0000000..69be8a9
--- /dev/null
+++ b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0002-Add-support-for-bootldr-images.patch
@@ -0,0 +1,239 @@
+From edc81e94e416e45b49c698456c9c6e5cd2caa041 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Wed, 10 Nov 2021 01:14:53 +0300
+Subject: [PATCH 2/4] Add support for bootldr images
+
+Add support for BOOTLDR! images found e.g. in the Pixel2/2XL binary
+blobs archive. They are used with the msm8998 (SDM835) Snapdragon SoCs.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+---
+ src/bootldr_image.c | 132 ++++++++++++++++++++++++++++++++++++++++
+ src/bootldr_image.h | 50 +++++++++++++++
+ src/qc_image_unpacker.c | 7 +++
+ 3 files changed, 189 insertions(+)
+ create mode 100644 src/bootldr_image.c
+ create mode 100644 src/bootldr_image.h
+
+diff --git a/src/bootldr_image.c b/src/bootldr_image.c
+new file mode 100644
+index 000000000000..739f6eb2a9c6
+--- /dev/null
++++ b/src/bootldr_image.c
+@@ -0,0 +1,132 @@
++/*
++
++ qc_image_unpacker
++ -----------------------------------------
++
++ Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
++ Copyright 2019 - 2020 by CENSUS S.A. All Rights Reserved.
++ Copyright 2021 by Linaro Ltd.
++
++ Licensed under the Apache License, Version 2.0 (the "License");
++ you may not use this file except in compliance with the License.
++ You may obtain a copy of the License at
++
++ http://www.apache.org/licenses/LICENSE-2.0
++
++ Unless required by applicable law or agreed to in writing, software
++ distributed under the License is distributed on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ See the License for the specific language governing permissions and
++ limitations under the License.
++
++*/
++
++#include "bootldr_image.h"
++
++#include "common.h"
++#include "utils.h"
++
++bool bootldr_image_detect(u1 *buf, size_t bufSz)
++{
++ bootldr_header_t *pBootLdrHeader;
++
++ if (bufSz < sizeof(bootldr_header_t))
++ return false;
++
++ pBootLdrHeader = (bootldr_header_t *)buf;
++
++ return (pBootLdrHeader->magic1 == BOOTLDR_IMG_MAGIC1) &&
++ (pBootLdrHeader->magic2 == BOOTLDR_IMG_MAGIC2);
++}
++
++bool bootldr_image_extract(u1 *buf, size_t bufSz, char *filePath, char *outputDir) {
++ bootldr_header_t *pBootLdrHeader;
++ bootldr_img_header_entry_t *pImgHeaderEntry;
++ u4 i = 0, j = 0, images, start_offset;
++ u1 *pImageEnd = NULL;
++ bool PnameTerminated = false;
++ char outPath[PATH_MAX], outFile[PATH_MAX];
++
++ if (bufSz < sizeof(bootldr_header_t)) {
++ LOGMSG(l_ERROR, "Invalid input size (%zu < %zu)", bufSz, sizeof(bootldr_header_t));
++ return false;
++ }
++
++ pBootLdrHeader = (bootldr_header_t *)buf;
++ if (pBootLdrHeader->magic1 != BOOTLDR_IMG_MAGIC1) {
++ LOGMSG(l_ERROR, "Invalid magic header (0x%x != 0x%x)", pBootLdrHeader->magic1, BOOTLDR_IMG_MAGIC1);
++ return false;
++ }
++
++ if (pBootLdrHeader->magic2 != BOOTLDR_IMG_MAGIC2) {
++ LOGMSG(l_ERROR, "Invalid magic header (0x%x != 0x%x)", pBootLdrHeader->magic2, BOOTLDR_IMG_MAGIC2);
++ return false;
++ }
++
++ pImgHeaderEntry = (bootldr_img_header_entry_t *)(buf + sizeof(bootldr_header_t));
++ images = pBootLdrHeader->images;
++ start_offset = pBootLdrHeader->start_offset;
++
++ if ((size_t)bufSz <= sizeof(bootldr_header_t) + images * sizeof(bootldr_img_header_entry_t)) {
++ LOGMSG(l_ERROR, "The size is smaller than image header size + entry size");
++ return false;
++ }
++
++ pImageEnd = buf + bufSz;
++
++ // Create output root directory to place extracted images
++ memset(outPath, 0, sizeof(outPath));
++ snprintf(outPath, sizeof(outPath), "%s/%s_images", outputDir, utils_fileBasename(filePath));
++ if (mkdir(outPath, 0755)) {
++ LOGMSG_P(l_ERROR, "mkdir(%s) failed", outPath);
++ return false;
++ }
++
++ LOGMSG(l_DEBUG, "Processing '%u' images", images);
++ for (i = 0; i < images; i++) {
++ int dstfd = -1;
++ PnameTerminated = false;
++ if (pImgHeaderEntry[i].ptn_name[0] == 0x00 ||
++ pImgHeaderEntry[i].size == 0)
++ break;
++
++ if (pImageEnd < buf + start_offset + pImgHeaderEntry[i].size) {
++ LOGMSG(l_ERROR, "Image size mismatch");
++ return false;
++ }
++
++ for (j = 0; j < BOOTLDR_PARTITION_NAME_SZ; j++) {
++ if (!(pImgHeaderEntry[i].ptn_name[j])) {
++ PnameTerminated = true;
++ break;
++ }
++ }
++ if (!PnameTerminated) {
++ LOGMSG(l_ERROR, "ptn_name string not terminated properly");
++ return false;
++ }
++
++ // Write output file
++ memset(outFile, 0, sizeof(outFile));
++ if (snprintf(outFile, sizeof(outFile), "%s/%s", outPath, pImgHeaderEntry[i].ptn_name) < 0) {
++ LOGMSG(l_ERROR, "Failed to construct output path string");
++ return false;
++ }
++ dstfd = open(outFile, O_CREAT | O_EXCL | O_RDWR, 0644);
++ if (dstfd == -1) {
++ LOGMSG_P(l_ERROR, "Couldn't create output file '%s' in input directory", outFile);
++ return false;
++ }
++
++ if (!utils_writeToFd(dstfd, buf + start_offset, pImgHeaderEntry[i].size)) {
++ close(dstfd);
++ return false;
++ }
++
++ close(dstfd);
++
++ start_offset += pImgHeaderEntry[i].size;
++ }
++
++ return true;
++}
+diff --git a/src/bootldr_image.h b/src/bootldr_image.h
+new file mode 100644
+index 000000000000..15e188491b79
+--- /dev/null
++++ b/src/bootldr_image.h
+@@ -0,0 +1,50 @@
++/*
++
++ qc_image_unpacker
++ -----------------------------------------
++
++ Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
++ Copyright 2019 - 2020 by CENSUS S.A. All Rights Reserved.
++ Copyright 2021 by Linaro Ltd.
++
++ Licensed under the Apache License, Version 2.0 (the "License");
++ you may not use this file except in compliance with the License.
++ You may obtain a copy of the License at
++
++ http://www.apache.org/licenses/LICENSE-2.0
++
++ Unless required by applicable law or agreed to in writing, software
++ distributed under the License is distributed on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ See the License for the specific language governing permissions and
++ limitations under the License.
++
++*/
++
++#ifndef _BOOTLDR_IMAGE_H_
++#define _BOOTLDR_IMAGE_H_
++
++#include "common.h"
++
++#define BOOTLDR_IMG_MAGIC1 0x544f4f42
++#define BOOTLDR_IMG_MAGIC2 0x2152444c
++#define BOOTLDR_PARTITION_NAME_SZ 0x40
++//#define MAX_IMAGES_IN_BOOTLDRIMG 32
++
++typedef struct bootldr_header {
++ u4 magic1;
++ u4 magic2;
++ u4 images;
++ u4 start_offset;
++ u4 full_size;
++} bootldr_header_t;
++
++typedef struct bootldr_img_header_entry {
++ char ptn_name[BOOTLDR_PARTITION_NAME_SZ];
++ u4 size;
++} bootldr_img_header_entry_t;
++
++bool bootldr_image_detect(u1 *, size_t);
++bool bootldr_image_extract(u1 *, size_t, char *, char *);
++
++#endif
+diff --git a/src/qc_image_unpacker.c b/src/qc_image_unpacker.c
+index 44d737803ea0..ea7fd0779645 100644
+--- a/src/qc_image_unpacker.c
++++ b/src/qc_image_unpacker.c
+@@ -25,6 +25,7 @@
+
+ #include "common.h"
+ #include "log.h"
++#include "bootldr_image.h"
+ #include "meta_image.h"
+ #include "packed_image.h"
+ #include "utils.h"
+@@ -152,6 +153,12 @@ int main(int argc, char **argv) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+ goto next_file;
+ }
++ } else if (bootldr_image_detect(buf, (size_t)fileSz)) {
++ LOGMSG(l_DEBUG, "bootldr image header found");
++ if (!bootldr_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
++ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
++ goto next_file;
++ }
+ } else {
+ LOGMSG(l_ERROR, "Invalid magic header 0x%x - skipping '%s'", *(u4*)buf,
+ pFiles.files[f]);
+--
+2.35.1
+
diff --git a/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0003-Do-not-let-dirname-tamper-with-inputFile.patch b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0003-Do-not-let-dirname-tamper-with-inputFile.patch
new file mode 100644
index 0000000..11bddd7
--- /dev/null
+++ b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0003-Do-not-let-dirname-tamper-with-inputFile.patch
@@ -0,0 +1,30 @@
+From 86690e6824c175e1fa7710ead34beb1d73c5a850 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Wed, 10 Nov 2021 01:21:46 +0300
+Subject: [PATCH 3/4] Do not let dirname tamper with inputFile
+
+The function dirname() can change passed string, resulting in
+pFiles.inputFile being changed. To prevent pFiles.inputFile change, pass
+a copy of the string to the dirname().
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+---
+ src/qc_image_unpacker.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/qc_image_unpacker.c b/src/qc_image_unpacker.c
+index ea7fd0779645..faddf6b1dd21 100644
+--- a/src/qc_image_unpacker.c
++++ b/src/qc_image_unpacker.c
+@@ -114,7 +114,7 @@ int main(int argc, char **argv) {
+ // It output directory not set, put extracted images under input directory
+ if (!pRunArgs.outputDir)
+ pRunArgs.outputDir =
+- utils_isValidDir(pFiles.inputFile) ? pFiles.inputFile : dirname(pFiles.inputFile);
++ utils_isValidDir(pFiles.inputFile) ? pFiles.inputFile : dirname(strdup(pFiles.inputFile));
+
+ size_t processedImgs = 0;
+ LOGMSG(l_INFO, "Processing %zu file(s) from %s", pFiles.fileCnt, pFiles.inputFile);
+--
+2.35.1
+
diff --git a/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0004-Fail-if-an-image-can-not-be-opened.patch b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0004-Fail-if-an-image-can-not-be-opened.patch
new file mode 100644
index 0000000..45ef04c
--- /dev/null
+++ b/recipes-devtools/qc-image-unpacker/qc-image-unpacker/0004-Fail-if-an-image-can-not-be-opened.patch
@@ -0,0 +1,63 @@
+From 9358721f0eed626d744437170b29dd6a18276ade Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Wed, 10 Nov 2021 01:29:29 +0300
+Subject: [PATCH 4/4] Fail if an image can not be opened
+
+In order to be sure that the images are really processed, fail if an
+image can not be opened.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+---
+ src/qc_image_unpacker.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/qc_image_unpacker.c b/src/qc_image_unpacker.c
+index faddf6b1dd21..bf5507bc011a 100644
+--- a/src/qc_image_unpacker.c
++++ b/src/qc_image_unpacker.c
+@@ -138,36 +138,36 @@ int main(int argc, char **argv) {
+ */
+ if ((size_t)fileSz < sizeof(u4)) {
+ LOGMSG(l_ERROR, "Invalid input size - skipping '%s'", pFiles.files[f]);
+- goto next_file;
++ exit(EXIT_FAILURE);
+ }
+
+ if (meta_image_detect(buf, (size_t)fileSz)) {
+ LOGMSG(l_DEBUG, "Meta image header found");
+ if (!meta_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+- goto next_file;
++ exit(EXIT_FAILURE);
+ }
+ } else if (packed_image_detect(buf, (size_t)fileSz)) {
+ LOGMSG(l_DEBUG, "packed image header found");
+ if (!packed_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+- goto next_file;
++ exit(EXIT_FAILURE);
+ }
+ } else if (bootldr_image_detect(buf, (size_t)fileSz)) {
+ LOGMSG(l_DEBUG, "bootldr image header found");
+ if (!bootldr_image_extract(buf, (size_t)fileSz, pFiles.files[f], pRunArgs.outputDir)) {
+ LOGMSG(l_ERROR, "Skipping '%s'", pFiles.files[f]);
+- goto next_file;
++ exit(EXIT_FAILURE);
+ }
+ } else {
+ LOGMSG(l_ERROR, "Invalid magic header 0x%x - skipping '%s'", *(u4*)buf,
+ pFiles.files[f]);
+- goto next_file;
++ exit(EXIT_FAILURE);
+ }
+
+ processedImgs++;
+
+- next_file:
++ //next_file:
+ // Clean-up
+ munmap(buf, fileSz);
+ buf = NULL;
+--
+2.35.1
+
diff --git a/recipes-devtools/qc-image-unpacker/qc-image-unpacker_git.bb b/recipes-devtools/qc-image-unpacker/qc-image-unpacker_git.bb
new file mode 100644
index 0000000..ab92253
--- /dev/null
+++ b/recipes-devtools/qc-image-unpacker/qc-image-unpacker_git.bb
@@ -0,0 +1,28 @@
+SUMMARY = "Android Qualcomm Image Unpacker"
+HOMEPAGE = "https://github.com/anestisb/qc_image_unpacker"
+SECTION = "devel"
+
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://../LICENSE;md5=138532bb21858341808df2740a1d13bf"
+
+SRC_URI = " \
+ git://github.com/anestisb/qc_image_unpacker;protocol=https;branch=master\
+ file://0001-Move-image-format-detection-to-separate-handlers.patch;patchdir=.. \
+ file://0002-Add-support-for-bootldr-images.patch;patchdir=.. \
+ file://0003-Do-not-let-dirname-tamper-with-inputFile.patch;patchdir=.. \
+ file://0004-Fail-if-an-image-can-not-be-opened.patch;patchdir=.. \
+"
+
+SRCREV = "dbaf73822205753c9a7722b330f74673cad183a5"
+
+PV = "0.2.0+${SRCPV}"
+S = "${WORKDIR}/git/src"
+
+DEPENDS = "zlib"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 0755 ${S}/qc_image_unpacker ${D}${bindir}
+}
+
+BBCLASSEXTEND = "native nativesdk"