bool MtkFormat::isValid(const unsigned char *data, std::size_t size) { // We have to parse the boot image so we can search for the mtk headers // Find Android header std::size_t headerIndex; if (!findHeader(data, size, 512, &headerIndex)) { return false; } // Check for header size overflow if (size < headerIndex + sizeof(BootImageHeader)) { return false; } // Read the Android boot image header auto hdr = reinterpret_cast<const BootImageHeader *>(&data[headerIndex]); uint32_t pos = 0; // Skip header pos += headerIndex; pos += sizeof(BootImageHeader); pos += skipPadding(sizeof(BootImageHeader), hdr->page_size); // Check for kernel size overflow if (pos + hdr->kernel_size > size) { return false; } if (hdr->kernel_size >= sizeof(MtkHeader)) { auto mtkHdr = reinterpret_cast<const MtkHeader *>(&data[pos]); if (std::memcmp(mtkHdr->magic, MTK_MAGIC, MTK_MAGIC_SIZE) == 0) { return true; } } // Skip kernel image pos += hdr->kernel_size; pos += skipPadding(hdr->kernel_size, hdr->page_size); // Check for ramdisk size overflow if (pos + hdr->ramdisk_size > size) { return false; } if (hdr->ramdisk_size >= sizeof(MtkHeader)) { auto mtkHdr = reinterpret_cast<const MtkHeader *>(&data[pos]); if (std::memcmp(mtkHdr->magic, MTK_MAGIC, MTK_MAGIC_SIZE) == 0) { return true; } } // Skip ramdisk image pos += hdr->ramdisk_size; pos += skipPadding(hdr->ramdisk_size, hdr->page_size); // There's no need to check any other images since the mtk header should // only exist for the kernel and ramdisk return false; }
/* * Skip the current member of the archive so that we are positioned * to tbe beginning of the next member's header (or end of file). * Returns TRUE on success. */ static BOOL skipMember(const Archive * arch) { if (lseek(arch->fd, arch->size, SEEK_CUR) == -1) { fprintf(stderr, "Can't skip past archive member: %s\n", strerror(errno)); return FALSE; } return skipPadding(arch->fd, arch->pad); }
/* * Copy all of the file data from the archive to the specified * open file. Returns TRUE on success. */ static BOOL writeFile(const Archive * arch, int outfd) { unsigned char buf[BUF_SIZE]; off_t n; n = arch->size; while (n > 0) { ssize_t cc; cc = read((int)arch->fd, (void *)buf, (size_t)MIN(n,(int) sizeof(buf))); if (cc == -1) { fprintf(stderr, "Error reading archive member: %s\n", strerror(errno)); return FALSE; } if (cc == 0) { fprintf(stderr, "Unexpected end of file\n"); return FALSE; } if (fullWrite(outfd,(const char *)buf,(int) cc) < 0) { fprintf(stderr, "Write error: %s\n", strerror(errno)); return FALSE; } n -= cc; } if (!skipPadding(arch->fd, arch->pad)) return FALSE; return TRUE; }
bool AndroidFormat::loadImage(const unsigned char *data, std::size_t size) { std::size_t headerIndex; if (!findHeader(data, size, 512, &headerIndex)) { LOGE("Failed to find Android header in boot image"); return false; } LOGD("Found Android boot image header at: %" PRIzu, headerIndex); if (!loadHeader(data, size, headerIndex)) { return false; } uint32_t pos = 0; // Save kernel image pos += headerIndex; pos += sizeof(BootImageHeader); pos += skipPadding(sizeof(BootImageHeader), mI10e->pageSize); if (pos + mI10e->hdrKernelSize > size) { LOGE("Kernel image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrKernelSize - size); return false; } mI10e->kernelImage.assign(data + pos, data + pos + mI10e->hdrKernelSize); // Save ramdisk image pos += mI10e->hdrKernelSize; pos += skipPadding(mI10e->hdrKernelSize, mI10e->pageSize); if (pos + mI10e->hdrRamdiskSize > size) { LOGE("Ramdisk image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrRamdiskSize - size); return false; } mI10e->ramdiskImage.assign(data + pos, data + pos + mI10e->hdrRamdiskSize); // Save second bootloader image pos += mI10e->hdrRamdiskSize; pos += skipPadding(mI10e->hdrRamdiskSize, mI10e->pageSize); if (pos + mI10e->hdrSecondSize > size) { LOGE("Second bootloader image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrSecondSize - size); return false; } // The second bootloader may not exist if (mI10e->hdrSecondSize > 0) { mI10e->secondImage.assign(data + pos, data + pos + mI10e->hdrSecondSize); } else { mI10e->secondImage.clear(); } // Save device tree image pos += mI10e->hdrSecondSize; pos += skipPadding(mI10e->hdrSecondSize, mI10e->pageSize); if (pos + mI10e->hdrDtSize > size) { std::size_t diff = pos + mI10e->hdrDtSize - size; LOGE("WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"); LOGE("THIS BOOT IMAGE MAY NO LONGER BE BOOTABLE. YOU HAVE BEEN WARNED"); LOGE("Device tree image exceeds boot image size by %" PRIzu " bytes and HAS BEEN TRUNCATED", diff); LOGE("WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"); mI10e->dtImage.assign(data + pos, data + pos + mI10e->hdrDtSize - diff); } else { mI10e->dtImage.assign(data + pos, data + pos + mI10e->hdrDtSize); } // The device tree image may not exist as well if (mI10e->hdrDtSize == 0) { mI10e->dtImage.clear(); } pos += mI10e->hdrDtSize; pos += skipPadding(mI10e->hdrDtSize, mI10e->pageSize); return true; }
bool AndroidFormat::createImage(std::vector<unsigned char> *dataOut) { BootImageHeader hdr; std::vector<unsigned char> data; memset(&hdr, 0, sizeof(BootImageHeader)); // Set header metadata fields memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); hdr.kernel_size = mI10e->hdrKernelSize; hdr.kernel_addr = mI10e->kernelAddr; hdr.ramdisk_size = mI10e->hdrRamdiskSize; hdr.ramdisk_addr = mI10e->ramdiskAddr; hdr.second_size = mI10e->hdrSecondSize; hdr.second_addr = mI10e->secondAddr; hdr.tags_addr = mI10e->tagsAddr; hdr.page_size = mI10e->pageSize; hdr.dt_size = mI10e->hdrDtSize; hdr.unused = mI10e->hdrUnused; // -1 for null byte std::strcpy(reinterpret_cast<char *>(hdr.name), mI10e->boardName.substr(0, BOOT_NAME_SIZE - 1).c_str()); std::strcpy(reinterpret_cast<char *>(hdr.cmdline), mI10e->cmdline.substr(0, BOOT_ARGS_SIZE - 1).c_str()); // Update SHA1 updateSha1Hash(&hdr, mI10e); switch (mI10e->pageSize) { case 2048: case 4096: case 8192: case 16384: case 32768: case 65536: case 131072: break; default: LOGE("Invalid page size: %u", mI10e->pageSize); return false; } // Header unsigned char *hdrBegin = reinterpret_cast<unsigned char *>(&hdr); data.insert(data.end(), hdrBegin, hdrBegin + sizeof(BootImageHeader)); // Padding uint32_t paddingSize = skipPadding(sizeof(BootImageHeader), hdr.page_size); data.insert(data.end(), paddingSize, 0); // Kernel image data.insert(data.end(), mI10e->kernelImage.begin(), mI10e->kernelImage.end()); // More padding paddingSize = skipPadding(mI10e->kernelImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); // Ramdisk image data.insert(data.end(), mI10e->ramdiskImage.begin(), mI10e->ramdiskImage.end()); // Even more padding paddingSize = skipPadding(mI10e->ramdiskImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); // Second bootloader image if (!mI10e->secondImage.empty()) { data.insert(data.end(), mI10e->secondImage.begin(), mI10e->secondImage.end()); // Enough padding already! paddingSize = skipPadding(mI10e->secondImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); } // Device tree image if (!mI10e->dtImage.empty()) { data.insert(data.end(), mI10e->dtImage.begin(), mI10e->dtImage.end()); // Last bit of padding (I hope) paddingSize = skipPadding(mI10e->dtImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); } dataOut->swap(data); return true; }
/* * Deserialize a HeapTuple's data from a byte-array. * * This code is based on the binary input handling functions in copy.c. */ HeapTuple DeserializeTuple(SerTupInfo * pSerInfo, StringInfo serialTup) { MemoryContext oldCtxt; TupleDesc tupdesc; HeapTuple htup; int natts; SerAttrInfo *attrInfo; uint32 attr_size; int i; StringInfoData attr_data; bool fHandled; AssertArg(pSerInfo != NULL); AssertArg(serialTup != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* * Flip to our tuple-serialization memory-context, to speed up memory * reclamation operations. */ AssertState(s_tupSerMemCtxt != NULL); oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Receive nulls character-array. */ pq_copymsgbytes(serialTup, pSerInfo->nulls, natts); skipPadding(serialTup); /* Deserialize the non-NULL attributes of this tuple */ initStringInfo(&attr_data); for (i = 0; i < natts; ++i) { attrInfo = pSerInfo->myinfo + i; if (pSerInfo->nulls[i]) /* NULL field. */ { pSerInfo->values[i] = (Datum) 0; continue; } /* * Assume that the data's output will be handled by the special IO * code, and if not then we can handle it the slow way. */ fHandled = true; switch (attrInfo->atttypid) { case INT4OID: pSerInfo->values[i] = Int32GetDatum(stringInfoGetInt32(serialTup)); break; case CHAROID: pSerInfo->values[i] = CharGetDatum(pq_getmsgbyte(serialTup)); skipPadding(serialTup); break; case BPCHAROID: case VARCHAROID: case INT2VECTOROID: /* postgres serialization logic broken, use our own */ case OIDVECTOROID: /* postgres serialization logic broken, use our own */ case ANYARRAYOID: { text *pText; int textSize; textSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (textSize + VARHDRSZ <= attrInfo->varlen_scratch_size) pText = (text *) attrInfo->pv_varlen_scratch; else pText = (text *) palloc(textSize + VARHDRSZ); #else pText = (text *) palloc(textSize + VARHDRSZ); #endif SET_VARSIZE(pText, textSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(pText), textSize); skipPadding(serialTup); pSerInfo->values[i] = PointerGetDatum(pText); break; } case DATEOID: { /* * TODO: I would LIKE to do something more efficient, but * DateADT is not strictly limited to 4 bytes by its * definition. */ DateADT date; pq_copymsgbytes(serialTup, (char *) &date, sizeof(DateADT)); skipPadding(serialTup); pSerInfo->values[i] = DateADTGetDatum(date); break; } case NUMERICOID: { /* * Treat the numeric as a varlena variable, and just push * the whole shebang to the output-buffer. We don't care * about the guts of the numeric. */ Numeric num; int numSize; numSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (numSize + VARHDRSZ <= attrInfo->varlen_scratch_size) num = (Numeric) attrInfo->pv_varlen_scratch; else num = (Numeric) palloc(numSize + VARHDRSZ); #else num = (Numeric) palloc(numSize + VARHDRSZ); #endif SET_VARSIZE(num, numSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(num), numSize); skipPadding(serialTup); pSerInfo->values[i] = NumericGetDatum(num); break; } case ACLITEMOID: { int aclSize, k, cnt; char *inputstring, *starsfree; aclSize = stringInfoGetInt32(serialTup); inputstring = (char*) palloc(aclSize + 1); starsfree = (char*) palloc(aclSize + 1); cnt = 0; pq_copymsgbytes(serialTup, inputstring, aclSize); skipPadding(serialTup); inputstring[aclSize] = '\0'; for(k=0; k<aclSize; k++) { if( inputstring[k] != '*') { starsfree[cnt] = inputstring[k]; cnt++; } } starsfree[cnt] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(aclitemin, CStringGetDatum(starsfree)); pfree(inputstring); break; } case 210: { int strsize; char *smgrstr; strsize = stringInfoGetInt32(serialTup); smgrstr = (char*) palloc(strsize + 1); pq_copymsgbytes(serialTup, smgrstr, strsize); skipPadding(serialTup); smgrstr[strsize] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(smgrin, CStringGetDatum(smgrstr)); break; } default: fHandled = false; } if (fHandled) continue; attr_size = stringInfoGetInt32(serialTup); /* reset attr_data to empty, and load raw data into it */ attr_data.len = 0; attr_data.data[0] = '\0'; attr_data.cursor = 0; appendBinaryStringInfo(&attr_data, pq_getmsgbytes(serialTup, attr_size), attr_size); skipPadding(serialTup); /* Call the attribute type's binary input converter. */ if (attrInfo->recv_finfo.fn_nargs == 1) pSerInfo->values[i] = FunctionCall1(&attrInfo->recv_finfo, PointerGetDatum(&attr_data)); else if (attrInfo->recv_finfo.fn_nargs == 2) pSerInfo->values[i] = FunctionCall2(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param)); else if (attrInfo->recv_finfo.fn_nargs == 3) pSerInfo->values[i] = FunctionCall3(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param), Int32GetDatum(tupdesc->attrs[i]->atttypmod) ); else { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("Conversion function takes %d args",attrInfo->recv_finfo.fn_nargs))); } /* Trouble if it didn't eat the whole buffer */ if (attr_data.cursor != attr_data.len) { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format"))); } } /* * Construct the tuple from the Datums and nulls values. NOTE: Switch * out of our temporary context before we form the tuple! */ MemoryContextSwitchTo(oldCtxt); htup = heap_form_tuple(tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextReset(s_tupSerMemCtxt); /* All done. Return the result. */ return htup; }
/* * Read the first member of the archive file and check whether it * is a special one, and if so, handle it. If the first member is * a normal archive member, then set up to rescan it for the next * readNormalMember call. Returns TRUE on success. */ static BOOL readSpecialMember(Archive * arch) { struct ar_hdr hdr; /* * 1. Read a header H. Fail if impossible. */ if (!readMember(arch, &hdr)) return FALSE; /* * 2. If H is a symbol table, ditch it. * Fail if impossible. */ if ((strncmp(hdr.ar_name, "/ ", 2) == 0) || (strncmp(hdr.ar_name, "__.SYMTAB ", sizeof(hdr.ar_name)) == 0)) { if (!canonicalize(arch, &hdr)) return FALSE; return skipMember(arch); } /* * 3. If H is a SysV longname table, read it into ARCH. */ if (strncmp(hdr.ar_name, "//", 2) == 0) { unsigned long len; ssize_t cc; if (!getNumber(hdr.ar_size, 10, sizeof(hdr.ar_size), &len)) { fprintf(stderr, "Invalid name-table size\n"); return FALSE; } arch->nameTable = malloc(len + 1); if (!arch->nameTable) { fprintf(stderr, "Out of memory\n"); return FALSE; } cc = read(arch->fd, arch->nameTable, len); if (cc == -1) { fprintf(stderr, "Error reading name-table: %s\n", strerror(errno)); return FALSE; } if (cc != (ssize_t) len) { fprintf(stderr, "Unexpected end of file in name-table\n"); return FALSE; } arch->nameTable[len] = 0; return skipPadding(arch->fd, len % 2); } /* * 4. We read a normal header. * Canonicalize it, and mark it as needing rescanning. */ arch->rescan = TRUE; return canonicalize(arch, &hdr); }
bool BumpFormat::isValid(const unsigned char *data, std::size_t size) { // We have to parse the boot image to find the end so we can compare the // trailing bytes to the bump magic string std::size_t headerIndex; if (!findHeader(data, size, 512, &headerIndex)) { return false; } // Check for header size overflow if (size < headerIndex + sizeof(BootImageHeader)) { return false; } // Read the Android boot image header auto hdr = reinterpret_cast<const BootImageHeader *>(&data[headerIndex]); uint32_t pos = 0; // Skip header pos += headerIndex; pos += sizeof(BootImageHeader); pos += skipPadding(sizeof(BootImageHeader), hdr->page_size); // Check for kernel size overflow if (pos + hdr->kernel_size > size) { return false; } // Skip kernel image pos += hdr->kernel_size; pos += skipPadding(hdr->kernel_size, hdr->page_size); // Check for ramdisk size overflow if (pos + hdr->ramdisk_size > size) { return false; } // Skip ramdisk image pos += hdr->ramdisk_size; pos += skipPadding(hdr->ramdisk_size, hdr->page_size); // Check for second bootloader size overflow if (pos + hdr->second_size > size) { return false; } // Skip second bootloader image pos += hdr->second_size; pos += skipPadding(hdr->second_size, hdr->page_size); // Check for device tree image size overflow if (pos + hdr->dt_size > size) { return false; } // Skip device tree image pos += hdr->dt_size; pos += skipPadding(hdr->dt_size, hdr->page_size); // We are now at the end of the boot image, so check for the bump magic return (size >= pos + BUMP_MAGIC_SIZE) && std::memcmp(data + pos, BUMP_MAGIC, BUMP_MAGIC_SIZE) == 0; }
/* * Deserialize a HeapTuple's data from a byte-array. * * This code is based on the binary input handling functions in copy.c. */ HeapTuple DeserializeTuple(SerTupInfo * pSerInfo, StringInfo serialTup) { MemoryContext oldCtxt; TupleDesc tupdesc; HeapTuple htup; int natts; SerAttrInfo *attrInfo; int i; AssertArg(pSerInfo != NULL); AssertArg(serialTup != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* * Flip to our tuple-serialization memory-context, to speed up memory * reclamation operations. */ AssertState(s_tupSerMemCtxt != NULL); oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Receive nulls character-array. */ pq_copymsgbytes(serialTup, pSerInfo->nulls, natts); skipPadding(serialTup); /* Deserialize the non-NULL attributes of this tuple */ for (i = 0; i < natts; ++i) { attrInfo = pSerInfo->myinfo + i; if (pSerInfo->nulls[i]) /* NULL field. */ { pSerInfo->values[i] = (Datum) 0; continue; } if (attrInfo->typlen == -1) { int32 sz; struct varlena *p; /* Read length first */ pq_copymsgbytes(serialTup, (char *) &sz, sizeof(int32)); if (sz < 0) elog(ERROR, "invalid length received for a varlen Datum"); p = palloc(sz + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(p), sz); SET_VARSIZE(p, sz + VARHDRSZ); pSerInfo->values[i] = PointerGetDatum(p); } else if (attrInfo->typlen == -2) { int32 sz; char *p; /* CString, with terminating '\0' included */ /* Read length first */ pq_copymsgbytes(serialTup, (char *) &sz, sizeof(int32)); if (sz < 0) elog(ERROR, "invalid length received for a CString"); p = palloc(sz + VARHDRSZ); /* Then data */ pq_copymsgbytes(serialTup, p, sz); pSerInfo->values[i] = CStringGetDatum(p); } else if (attrInfo->typbyval) { /* Read a whole Datum */ pq_copymsgbytes(serialTup, (char *) &(pSerInfo->values[i]), sizeof(Datum)); } else { /* fixed width, pass-by-ref */ char *p = palloc(attrInfo->typlen); pq_copymsgbytes(serialTup, p, attrInfo->typlen); pSerInfo->values[i] = PointerGetDatum(p); } } /* * Construct the tuple from the Datums and nulls values. NOTE: Switch * out of our temporary context before we form the tuple! */ MemoryContextSwitchTo(oldCtxt); htup = heap_form_tuple(tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextReset(s_tupSerMemCtxt); /* Trouble if it didn't eat the whole buffer */ if (serialTup->cursor != serialTup->len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format"))); /* All done. Return the result. */ return htup; }
bool MtkFormat::createImage(std::vector<unsigned char> *dataOut) { BootImageHeader hdr; std::vector<unsigned char> data; memset(&hdr, 0, sizeof(BootImageHeader)); bool hasKernelHdr = !mI10e->mtkKernelHdr.empty(); bool hasRamdiskHdr = !mI10e->mtkRamdiskHdr.empty(); // Check header sizes if (hasKernelHdr && mI10e->mtkKernelHdr.size() != sizeof(MtkHeader)) { LOGE("Expected %" PRIzu " byte kernel MTK header, but have %" PRIzu " bytes", sizeof(MtkHeader), mI10e->mtkKernelHdr.size()); return false; } if (hasRamdiskHdr && mI10e->mtkRamdiskHdr.size() != sizeof(MtkHeader)) { LOGE("Expected %" PRIzu " byte ramdisk MTK header, but have %" PRIzu " bytes", sizeof(MtkHeader), mI10e->mtkRamdiskHdr.size()); return false; } std::size_t kernelSize = mI10e->kernelImage.size() + mI10e->mtkKernelHdr.size(); std::size_t ramdiskSize = mI10e->ramdiskImage.size() + mI10e->mtkRamdiskHdr.size(); MtkHeader mtkKernelHdr; MtkHeader mtkRamdiskHdr; if (hasKernelHdr) { std::memcpy(&mtkKernelHdr, mI10e->mtkKernelHdr.data(), sizeof(MtkHeader)); mtkKernelHdr.size = mI10e->kernelImage.size(); } if (hasRamdiskHdr) { std::memcpy(&mtkRamdiskHdr, mI10e->mtkRamdiskHdr.data(), sizeof(MtkHeader)); mtkRamdiskHdr.size = mI10e->ramdiskImage.size(); } // Set header metadata fields memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); hdr.kernel_size = kernelSize; hdr.kernel_addr = mI10e->kernelAddr; hdr.ramdisk_size = ramdiskSize; hdr.ramdisk_addr = mI10e->ramdiskAddr; hdr.second_size = mI10e->hdrSecondSize; hdr.second_addr = mI10e->secondAddr; hdr.tags_addr = mI10e->tagsAddr; hdr.page_size = mI10e->pageSize; hdr.dt_size = mI10e->hdrDtSize; hdr.unused = mI10e->hdrUnused; // -1 for null byte std::strcpy(reinterpret_cast<char *>(hdr.name), mI10e->boardName.substr(0, BOOT_NAME_SIZE - 1).c_str()); std::strcpy(reinterpret_cast<char *>(hdr.cmdline), mI10e->cmdline.substr(0, BOOT_ARGS_SIZE - 1).c_str()); // Update SHA1 updateSha1Hash(&hdr, mI10e, hasKernelHdr ? &mtkKernelHdr : nullptr, hasRamdiskHdr ? &mtkRamdiskHdr : nullptr, kernelSize, ramdiskSize); switch (mI10e->pageSize) { case 2048: case 4096: case 8192: case 16384: case 32768: case 65536: case 131072: break; default: LOGE("Invalid page size: %u", mI10e->pageSize); return false; } // Header unsigned char *hdrBegin = reinterpret_cast<unsigned char *>(&hdr); data.insert(data.end(), hdrBegin, hdrBegin + sizeof(BootImageHeader)); // Padding uint32_t paddingSize = skipPadding(sizeof(BootImageHeader), hdr.page_size); data.insert(data.end(), paddingSize, 0); // Kernel image if (hasKernelHdr) { data.insert(data.end(), reinterpret_cast<const unsigned char *>(&mtkKernelHdr), reinterpret_cast<const unsigned char *>(&mtkKernelHdr) + sizeof(MtkHeader)); } data.insert(data.end(), mI10e->kernelImage.begin(), mI10e->kernelImage.end()); // More padding paddingSize = skipPadding(kernelSize, hdr.page_size); data.insert(data.end(), paddingSize, 0); // Ramdisk image if (hasRamdiskHdr) { data.insert(data.end(), reinterpret_cast<const unsigned char *>(&mtkRamdiskHdr), reinterpret_cast<const unsigned char *>(&mtkRamdiskHdr) + sizeof(MtkHeader)); } data.insert(data.end(), mI10e->ramdiskImage.begin(), mI10e->ramdiskImage.end()); // Even more padding paddingSize = skipPadding(ramdiskSize, hdr.page_size); data.insert(data.end(), paddingSize, 0); // Second bootloader image if (!mI10e->secondImage.empty()) { data.insert(data.end(), mI10e->secondImage.begin(), mI10e->secondImage.end()); // Enough padding already! paddingSize = skipPadding(mI10e->secondImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); } // Device tree image if (!mI10e->dtImage.empty()) { data.insert(data.end(), mI10e->dtImage.begin(), mI10e->dtImage.end()); // Last bit of padding (I hope) paddingSize = skipPadding(mI10e->dtImage.size(), hdr.page_size); data.insert(data.end(), paddingSize, 0); } dataOut->swap(data); return true; }