From 8986984b5d618be0b753e55d6b9d9d1744bc4094 Mon Sep 17 00:00:00 2001 From: Alexander Diewald Date: Thu, 6 Oct 2016 22:04:05 +0200 Subject: [PATCH] libbootimg: Add support for newer Sony ELF boot images The boot image found in sony firmwares (stock) is different to the already implemented version. This change adds support for these images (found in the Z2) by introducing more flexibility in the ELF handling and the output format (ELF --> ANDROID!) In particular: - Use a more dynamic structure to describe elf files: Add handling information (e.g., the elf version) and separate structures for the ELF header, the program headers, and the section headers - Adjust the read/load operations to account for the new structure - Add handling code for the differentiation between the two known ELF versions - Adjust the writing code such that it can output the content of an ELF image as a standard ANDROID! image: The newer ELF versions are not booted if a single (unused) bit is modifed Possibly the (unlocked) bootloader performs a signature check only for ELF files - Add some debug code that allows to print either to stdout or the kernel log (useful for on-device debugging) - Extend the Sony Stock support in a dynamic way - Add dependencies for klog_write - Also fix all minor warnings The code has been tested with the boot image of an Xperia Z2, and on the devices from the original ELF implementation (SP) Change-Id: I0e9a41ae6ce26bf4608ef86cec1e2735f746abb6 Signed-off-by: Alexander Diewald Signed-off-by: Adrian DC --- Android.mk | 13 +- include/boot_img_hdr.h | 124 ++++---- include/libbootimg.h | 120 +++++++- src/libbootimg.c | 653 +++++++++++++++++++++++++++++++++++------ 4 files changed, 766 insertions(+), 144 deletions(-) diff --git a/Android.mk b/Android.mk index d7b4251..5a14e38 100644 --- a/Android.mk +++ b/Android.mk @@ -13,10 +13,12 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libc +LOCAL_CFLAGS := -DDEBUG_KMSG +LOCAL_STATIC_LIBRARIES := libc libcutils include $(BUILD_EXECUTABLE) +# bbootimge_host include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(bbootimg_src_file) @@ -36,14 +38,19 @@ LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_MODULE_STEM := bbootimg LOCAL_SRC_FILES:= src/bbootimg.c src/libbootimg.c -LOCAL_SHARED_LIBRARIES := libc -include $(BUILD_EXECUTABLE) +LOCAL_CFLAGS := -DDEBUG_KMSG +LOCAL_SHARED_LIBRARIES := libc libcutils +include $(BUILD_EXECUTABLE) # libbootimg include $(CLEAR_VARS) + LOCAL_SRC_FILES := src/libbootimg.c LOCAL_MODULE := libbootimg LOCAL_MODULE_TAGS := eng + +LOCAL_CFLAGS := -DDEBUG_KMSG + include $(BUILD_STATIC_LIBRARY) diff --git a/include/boot_img_hdr.h b/include/boot_img_hdr.h index 1f7f59e..6ce3a33 100644 --- a/include/boot_img_hdr.h +++ b/include/boot_img_hdr.h @@ -20,7 +20,7 @@ #include /* -** +-----------------+ +** +-----------------+ ** | boot header | 1 page ** +-----------------+ ** | kernel | n pages @@ -51,11 +51,24 @@ #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 -#define BOOT_MAGIC_ELF "ELF" -#define BOOT_MAGIC_ELF_SIZE 3 #define BOOT_NAME_SIZE 16 #define BOOT_ARGS_SIZE 512 +#define BOOT_MAGIC_ELF "ELF" +#define BOOT_MAGIC_ELF_SIZE 3 +#define VER_ELF_1 (1 << 0) +#define VER_ELF_2 (1 << 1) + +#define OUT_ELF (1 << 0) /* Same output format: ELF container */ +#define OUT_AND (1 << 1) /* Different output format: standard Android container */ + +#define ELF_PROG_KER 0 +#define ELF_PROG_RAM 1 +#define ELF_PROG_RPM 2 +#define ELF_PROG_CMD 3 + +#define ELF_SECT_CMD 0 + struct boot_img_hdr { uint8_t magic[BOOT_MAGIC_SIZE]; @@ -81,59 +94,64 @@ struct boot_img_hdr uint32_t id[8]; /* timestamp / checksum / sha1 / etc */ }; -struct boot_img_hdr_elf +struct boot_img_elf_hdr { uint8_t magic[8]; /* .ELF (0x00 to 0x07) */ - uint8_t unused[8]; /* unused chars */ - uint16_t type; /* boot type */ - uint16_t machine; /* boot machine */ - uint32_t version; /* boot version */ - uint32_t entry_addr; /* boot entry */ - uint32_t phoff; /* boot phoff */ - uint32_t shoff; /* boot shoff */ - uint32_t flags; /* boot flags */ - uint16_t ehsize; /* boot ehsize */ - uint16_t phentsize; /* boot phentsize */ - uint16_t phnum; /* boot phnum */ - uint16_t shentsize; /* boot shentsize */ - uint16_t shnum; /* boot shnum */ - uint16_t shstrndx; /* boot shstrndx */ - uint32_t kernel_type; /* kernel type (0x34 to 0x37) */ - uint32_t kernel_offset; /* kernel offset (0x38 to 0x3B) */ - uint32_t kernel_vaddr; /* kernel address (0x3C to 0x3F) */ - uint32_t kernel_paddr; /* kernel address duplicate */ - uint32_t kernel_size; /* kernel size (0x44 to 0x47) */ - uint32_t kernel_msize; /* kernel size duplicate */ - uint32_t kernel_flags; /* kernel flags (0x4C to 0x4F) */ - uint32_t kernel_align; /* kernel alignment */ - uint32_t ramdisk_type; /* kernel type (0x54) */ - uint32_t ramdisk_offset; /* ramdisk offset (0x58 to 0x5B) */ - uint32_t ramdisk_vaddr; /* ramdisk address (0x5C to 0x5F) */ - uint32_t ramdisk_paddr; /* ramdisk address duplicate */ - uint32_t ramdisk_size; /* ramdisk size (0x64 to 0x67) */ - uint32_t ramdisk_msize; /* ramdisk size duplicate */ - uint32_t ramdisk_flags; /* ramdisk flags (0x6C to 0x6F) */ - uint32_t ramdisk_align; /* cmdline alignment */ - uint32_t rpm_type; /* rpm type (0x74 to 0x77) */ - uint32_t rpm_offset; /* rpm offset (0x78 to 0x7B) */ - uint32_t rpm_vaddr; /* rpm address (0x7C to 0x7F) */ - uint32_t rpm_paddr; /* rpm address duplicate */ - uint32_t rpm_size; /* rpm size (0x84 to 0x87) */ - uint32_t rpm_msize; /* rpm size duplicate */ - uint32_t rpm_flags; /* rpm flags (0x8C to 0x8F) */ - uint32_t rpm_align; /* rpm alignment */ - uint32_t cmd_type; /* cmdline type (0x94 to 0x97) */ - uint32_t cmd_offset; /* cmdline offset (0x98 to 0x9B) */ - uint32_t cmd_vaddr; /* cmdline address (0x9C to 0x9F) */ - uint32_t cmd_paddr; /* cmdline address duplicate */ - uint32_t cmd_size; /* cmdline size (0xA4 to 0xA7) */ - uint32_t cmd_msize; /* cmdline size duplicate */ - uint32_t cmd_flags; /* cmdline flags (0xAC to 0xAF) */ - uint32_t cmd_align; /* cmdline alignment */ - uint8_t header_vals[3900]; /* header additional values */ - uint8_t name[BOOT_NAME_SIZE]; /* added - asciiz product name */ + uint8_t unused[8]; /* unused chars (0x08 to 0x0F) */ + uint16_t type; /* boot type (0x10 to 0x11) */ + uint16_t machine; /* boot machine (0x12 to 0x13) */ + uint32_t version; /* boot version (0x14 to 0x17) */ + uint32_t entry_addr; /* boot entry (0x18 to 0x1B) */ + uint32_t phoff; /* boot phoff (0x1C to 0x1F) */ + uint32_t shoff; /* boot shoff (0x20 to 0x23) */ + uint32_t flags; /* boot flags (0x24 to 0x27) */ + uint16_t ehsize; /* boot ehsize (0x28 to 0x29) */ + uint16_t phentsize; /* boot phentsize (0x2A to 0x2B) */ + uint16_t phnum; /* boot phnum (0x2C to 0x2D) */ + uint16_t shentsize; /* boot shentsize (0x2E to 0x2F)*/ + uint16_t shnum; /* boot shnum (0x30 to 0x31) */ + uint16_t shstrndx; /* boot shstrndx (0x32 to 0x33) */ +}; + +struct boot_img_elf_info +{ + struct boot_img_elf_hdr hdr; /* The ELF file header. */ + struct boot_img_elf_prog_hdr* prog; /* The program header entries. */ + struct boot_img_elf_sect_hdr* sect; /* The section header entries. */ + struct boot_img_elf_misc_hdr* misc; /* Miscellaneous information found in some ELF versions. */ + uint8_t elf_version; + uint8_t elf_out_format; + uint32_t cmdline_size; +}; + +struct boot_img_elf_prog_hdr +{ + uint32_t type; /* type (position + 0x0 to 0x3) */ + uint32_t offset; /* offset (position + 0x4 to 0x7) */ + uint32_t vaddr; /* address (position + 0x8 to 0xB) */ + uint32_t paddr; /* address duplicate (position + 0xC to 0xF) */ + uint32_t size; /* size (position + 0x10 to 0x13) */ + uint32_t msize; /* size duplicate (position + 0x14 to 0x17) */ + uint32_t flags; /* flags (position + 0x18 to 0x1B) */ + uint32_t align; /* alignment (position + 0x1C to 0x1F)*/ }; -typedef struct boot_img_hdr boot_img_hdr; +struct boot_img_elf_sect_hdr +{ + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t addr; + uint32_t offset; + uint32_t size; + uint8_t misc[16]; +}; + +struct boot_img_elf_misc_hdr +{ + uint8_t* data; /* header additional data */ + uint32_t data_size; /* header additional size */ + uint8_t name[BOOT_NAME_SIZE]; /* added - asciiz product name */ +}; #endif diff --git a/include/libbootimg.h b/include/libbootimg.h index 1aed9d1..dd4efe0 100644 --- a/include/libbootimg.h +++ b/include/libbootimg.h @@ -11,11 +11,20 @@ extern "C" { #include #include +#include #include "boot_img_hdr.h" #define LIBBOOTIMG_VERSION 0x000203 // 0xMMNNPP #define LIBBOOTIMG_VERSION_STR "0.2.3" +#ifdef DEBUG_KMSG +#define LOG_DBG(fmt, ...) klog_write(3, "<3>%s: " fmt, "libbootimg", ##__VA_ARGS__) +#elif DEBUG_STDOUT +#define LOG_DBG printf("libbootimg: "); printf +#else +#define LOG_DBG(fmt, ...) "" +#endif + /** * Enum containing possible blob types in a boot image. */ @@ -84,10 +93,12 @@ struct bootimg_blob struct bootimg { struct boot_img_hdr hdr; /*!< Boot image header */ - struct bootimg_blob blobs[LIBBOOTIMG_BLOB_CNT]; /*!< Blobs packed in the boot image. */ - int start_offset; /*!< Offset of the boot image structure from the start of the file. Only used when loading blobs from boot.img file. */ - struct boot_img_hdr_elf* hdr_elf; /*!< Boot image header in ELF format */ + struct bootimg_blob blob; /* Complete blob */ + uint32_t blob_size; /* Size of the complete blob */ + struct bootimg_blob blobs[LIBBOOTIMG_BLOB_CNT]; /*!< Blobs packed in the boot image */ + int start_offset; /*!< Offset of the boot image structure from the start of the file. Only used when loading blobs from boot.img file */ uint8_t is_elf; /*!< Select the ELF boot image format */ + struct boot_img_elf_info* hdr_info; /*!< Boot image meta-information for ELF formats */ }; /** @@ -117,6 +128,62 @@ int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_ma */ int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path); +/** + * Determines the ELF boot image version (custom definition) and the + * required output format (ELF or ANDROID!) based on the number of + * program headers given by the ELF header. + * @param hdr_info pointer to the structure holding the read header information + */ +void libbootimg_get_elf_version(struct boot_img_elf_info *hdr_info); + +/** + * Reads the program headers from the given ELF file and adds the content + * of each header to the given structure. + * @param hdr_info structure holding the meta-information of the given ELF file + * @param f pointer to the file descriptor of the ELF file + * @return zero on success or the error code returned by the file operations. + */ +int libbootimg_load_elf_prog_header(struct boot_img_elf_info *hdr_info, FILE *f); + +/** + * Reads the section headers from the given ELF file and adds the content + * of each header to the given structure. + * @param hdr_info structure holding the meta-information of the given ELF file + * @param f pointer to the file descriptor of the ELF file + * @return zero on success or the error code returned by the file operations. + */ +int libbootimg_load_elf_sect_header(struct boot_img_elf_info *hdr_info, FILE *f); + +/** + * Reads the miscellaneous information from the given ELF file and adds the content + * to the given structure. This information is present in some ELF versions and + * required for them to operate properly (version 1). + * @param hdr_info structure holding the meta-information of the given ELF file + * @param size of the bootimage pagesize needed for alignment + * @param f pointer to the file descriptor of the ELF file + * @return one on success or the error code returned by the file operations. + */ +int libbootimg_load_elf_misc_header(struct boot_img_elf_info *hdr_info, uint32_t page_size, FILE *f); + +/** + * Extracts the kernel boot command line from an ELF file and adds it to + * the generic structure describing the blob (-> hdr). + * @param hdr pointer to boot_img_hdr structure + * @param elf_info structure holding the meta-information of the given ELF file. + * @param f pointer to the file descriptor of the ELF file. + */ +void libbootimg_read_cmdline(struct boot_img_hdr *hdr, struct boot_img_elf_info *elf_info, FILE *f); + +/** + * Returns a pointer referencing the ELF program header which describes the content + * of a given type like the kernel or ramdisk part of a boot image. + * @param hdr_info structure holding the meta-information of the given ELF file. + * @param type integer value that describes the desired pointer as given in the enum + * {@link libbootimg_blob_type}. + * @return pointer to the program header describing the desired part of the boot image. + */ +struct boot_img_elf_prog_hdr* get_elf_proc_hdr_of(struct boot_img_elf_info *elf_info, int type); + /** * Loads boot_img_hdr or boot_img_hdr_elf from file on disk * @param hdr pointer to boot_img_hdr structure @@ -127,7 +194,7 @@ int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path); * successful, negative value from libbootimg_error if failed. */ int libbootimg_load_headers(struct boot_img_hdr *hdr, - struct boot_img_hdr_elf *hdr_elf, uint8_t *is_elf, const char *path); + struct boot_img_elf_info *hdr_elf, uint8_t *is_elf, const char *path); /** * Updates the header addresses to the blobs. @@ -142,8 +209,6 @@ int libbootimg_update_headers(struct bootimg *b); */ void libbootimg_destroy(struct bootimg *b); - - /** * Writes blob to a file. * @param blob pointer to source struct bootimg_blob @@ -244,6 +309,14 @@ int libbootimg_write_img(struct bootimg *b, const char *dest); */ int libbootimg_write_img_fileptr(struct bootimg *b, FILE *f); +/** + * Writes boot image to a file: Updated implementation that effectively *injects* data into an existing boot image + * @param b pointer to struct bootimg + * @param f pointer to FILE to write data into + * @return number of bytes written to the file if successful, negative value from libbootimg_error if failed. + */ +int libbootimg_write_img_fileptr_new(struct bootimg *b, FILE *f); + /** * Writes boot image to a file and then calls libbootimg_destroy. * The bootimg struct is destroyed even if this function fails. @@ -274,6 +347,41 @@ const char *libbootimg_version_str(void); */ const char *libbootimg_error_str(int error); +/** + * Prints the content of the boot image information to the stdout or + * the kernel log (dmesg). + * @param hdr pointer to the boot image information. + */ +void print_hdr_to_log(struct boot_img_hdr* hdr); + +/** + * Prints the content of an elf header described by the given structure + * to the stdout or the kernel log (dmesg). + * @param elf_info pointer to the elf header information. + */ +void print_elf_hdr_to_log(struct boot_img_elf_info* elf_info); + +/** + * Prints the content of an elf program header described by the given structure + * to the stdout or the kernel log (dmesg). + * @param elf_prog_hdr pointer to the program header information. + */ +void print_elf_prog_hdr_to_log(struct boot_img_elf_prog_hdr* elf_prog_hdr); + +/** + * Prints the content of an elf section header described by the given structure + * to the stdout or the kernel log (dmesg). + * @param elf_sect_hdr pointer to the section header information. + */ +void print_elf_sect_hdr_to_log(struct boot_img_elf_sect_hdr* elf_sect_hdr); + +/** + * Prints the content of an elf misc header described by the given structure + * to the stdout or the kernel log (dmesg). + * @param elf_sect_hdr pointer to the section header information. + */ +void print_elf_misc_hdr_to_log(struct boot_img_elf_misc_hdr* elf_misc_hdr); + #ifdef __cplusplus } #endif diff --git a/src/libbootimg.c b/src/libbootimg.c index 12a5df0..ebd80f6 100644 --- a/src/libbootimg.c +++ b/src/libbootimg.c @@ -72,8 +72,13 @@ static void fill_id_hashes(struct bootimg *b) int i = 0; // hash blobs - for(; i < LIBBOOTIMG_BLOB_CNT && i < 5; ++i) - b->hdr.id[i] = calc_fnv_hash(b->blobs[i].data, *b->blobs[i].size); + for (; i < LIBBOOTIMG_BLOB_CNT ; ++i) + { + if (b->blobs[i].size != NULL) + { + b->hdr.id[i] = calc_fnv_hash(b->blobs[i].data, *b->blobs[i].size); + } + } // hash kernel, ramdisk and second _addr and _size together b->hdr.id[i++] = calc_fnv_hash(&b->hdr.kernel_size, sizeof(uint32_t)*6); @@ -92,8 +97,8 @@ void libbootimg_init_new(struct bootimg *img) memset(img, 0, sizeof(struct bootimg)); memcpy(img->hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); img->hdr.page_size = DEFAULT_PAGE_SIZE; - img->hdr_elf = malloc(sizeof(struct boot_img_hdr_elf)); - memset(img->hdr_elf, sizeof(struct boot_img_hdr_elf), 0); + img->hdr_info = malloc(sizeof(struct boot_img_elf_info)); + memset(img->hdr_info, sizeof(struct boot_img_elf_info), 0); img->is_elf = 0; img->blobs[LIBBOOTIMG_BLOB_KERNEL].size = &img->hdr.kernel_size; @@ -102,6 +107,30 @@ void libbootimg_init_new(struct bootimg *img) img->blobs[LIBBOOTIMG_BLOB_DTB].size = &img->hdr.dt_size; } +int libbootimg_read_blob(int64_t addr, struct bootimg_blob* blob, FILE* f) +{ + int res = 0; + if (fseek(f, addr, SEEK_SET) < 0) + { + if (errno == EINVAL) + { + res = LIBBOOTIMG_ERROR_IMG_EOF; + } + else + { + res = translate_errnum(errno); + } + return res; + } + blob->data = malloc(*blob->size); + if (fread(blob->data, *blob->size, 1, f) != 1) + { + res = translate_fread_error(f); + return res; + } + return res; +} + int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_mask) { int i; @@ -112,7 +141,7 @@ int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_ma libbootimg_init_new(img); - res = libbootimg_load_headers(&img->hdr, img->hdr_elf, &img->is_elf, path); + res = libbootimg_load_headers(&img->hdr, img->hdr_info, &img->is_elf, path); if(res < 0) { libbootimg_destroy(img); @@ -127,38 +156,27 @@ int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_ma addr = img->start_offset + img->hdr.page_size; - for(i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i) + for (i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i) { blob = &img->blobs[i]; - if((load_blob_mask & (1 << i)) && *blob->size != 0) + if (img->is_elf) { - if(fseek(f, addr, SEEK_SET) < 0) + struct boot_img_elf_prog_hdr *cur_prog_hdr = get_elf_proc_hdr_of(img->hdr_info, i); + if (cur_prog_hdr != NULL) { - if(errno == EINVAL) - res = LIBBOOTIMG_ERROR_IMG_EOF; - else - res = translate_errnum(errno); - goto fail; - } - - blob->data = malloc(*blob->size); - - if(fread(blob->data, *blob->size, 1, f) != 1) - { - res = translate_fread_error(f); - goto fail; + res = libbootimg_read_blob(cur_prog_hdr->offset, blob, f); } } + else if ((load_blob_mask & (1 << i)) && *blob->size != 0) + { + res = libbootimg_read_blob(addr, blob, f); + } if (!img->is_elf) { addr += align_size(*blob->size, img->hdr.page_size); } - else - { - addr += *blob->size; - } } fclose(f); @@ -174,7 +192,7 @@ void libbootimg_destroy(struct bootimg *b) { struct bootimg_blob *blob = b->blobs; struct bootimg_blob * const blobs_end = blob + LIBBOOTIMG_BLOB_CNT; - free(b->hdr_elf); + free(b->hdr_info); for(; blob != blobs_end; ++blob) { free(blob->data); @@ -184,12 +202,12 @@ void libbootimg_destroy(struct bootimg *b) int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path) { - struct boot_img_hdr_elf hdr_elf; + struct boot_img_elf_info hdr_elf; return libbootimg_load_headers(hdr, &hdr_elf, NULL, path); } int libbootimg_load_headers(struct boot_img_hdr *hdr, - struct boot_img_hdr_elf *hdr_elf, uint8_t *is_elf, const char *path) + struct boot_img_elf_info *hdr_info, uint8_t *is_elf, const char *path) { int res = 0; FILE *f; @@ -219,33 +237,98 @@ int libbootimg_load_headers(struct boot_img_hdr *hdr, res = known_magic_pos[i]; break; } - else if (hdr_elf != NULL && memcmp(hdr->magic + 1, BOOT_MAGIC_ELF, + else if (hdr_info != NULL && memcmp(hdr->magic + 1, BOOT_MAGIC_ELF, BOOT_MAGIC_ELF_SIZE) == 0) { fseek(f, 0, SEEK_SET); - if (fread(hdr_elf, sizeof(struct boot_img_hdr_elf), 1, f) == 1) + if (fread(&hdr_info->hdr, sizeof(struct boot_img_elf_hdr), 1, f) == 1) { - hdr->kernel_size = hdr_elf->kernel_size; - hdr->kernel_addr = hdr_elf->kernel_offset; - hdr->ramdisk_size = hdr_elf->ramdisk_size; - hdr->ramdisk_addr = hdr_elf->ramdisk_offset; - hdr->second_size = hdr_elf->rpm_size; - hdr->second_addr = hdr_elf->rpm_offset; - hdr->page_size = hdr_elf->kernel_offset; - hdr->tags_addr = 0; - hdr->dt_size = 0; hdr->id[0] = '\0'; - memcpy(hdr->name, hdr_elf->name, BOOT_NAME_SIZE); - cmd_len = hdr_elf->cmd_size; - memset(hdr->cmdline, '\0', BOOT_ARGS_SIZE); - fseek(f, hdr_elf->cmd_offset, SEEK_SET); - fread(hdr->cmdline, cmd_len < BOOT_ARGS_SIZE ? - cmd_len : BOOT_ARGS_SIZE, 1, f); + res = known_magic_pos[i]; if (is_elf != NULL) { *is_elf = 1; + print_elf_hdr_to_log(hdr_info); + } + + libbootimg_get_elf_version(hdr_info); + + res = libbootimg_load_elf_prog_header(hdr_info, f); + if (res != 1) + { + break; } + + // Update the standard boot image meta-information with + // the information found in the elf file + struct boot_img_elf_prog_hdr *kernel_hdr = get_elf_proc_hdr_of(hdr_info, + LIBBOOTIMG_BLOB_KERNEL); + hdr->kernel_size = kernel_hdr->size; + hdr->kernel_addr = kernel_hdr->offset; + + struct boot_img_elf_prog_hdr *ramdisk_hdr = get_elf_proc_hdr_of(hdr_info, + LIBBOOTIMG_BLOB_RAMDISK); + hdr->ramdisk_size = ramdisk_hdr->size; + hdr->ramdisk_addr = ramdisk_hdr->offset; + + struct boot_img_elf_prog_hdr *second_hdr = get_elf_proc_hdr_of(hdr_info, + LIBBOOTIMG_BLOB_SECOND); + if (second_hdr != NULL) + { + hdr->second_size = second_hdr->size; + hdr->second_addr = second_hdr->offset; + } + else + { + hdr->second_size = 0; + hdr->second_addr = 0; + } + + struct boot_img_elf_prog_hdr *dtb_hdr = get_elf_proc_hdr_of(hdr_info, + LIBBOOTIMG_BLOB_DTB); + if (dtb_hdr != NULL) + { + hdr->dt_size = dtb_hdr->size; + hdr->tags_addr = dtb_hdr->offset; + } + else + { + hdr->dt_size = 0; + hdr->tags_addr = 0; + } + + // ELF files version 1 are page aligned, version 2 are not + if (hdr_info->elf_version == VER_ELF_1) + { + hdr->page_size = hdr->kernel_addr; + } + else + { + hdr->page_size = DEFAULT_PAGE_SIZE; + } + + if (hdr_info->elf_version == VER_ELF_1) + { + res = libbootimg_load_elf_misc_header(hdr_info, hdr->page_size, f); + if (res != 1) + { + break; + } + memcpy(hdr->name, hdr_info->misc->name, BOOT_NAME_SIZE); + } + else if (hdr_info->elf_version == VER_ELF_2) + { + res = libbootimg_load_elf_sect_header(hdr_info, f); + if (res != 1) + { + break; + } + } + + libbootimg_read_cmdline(hdr, hdr_info, f); + + print_hdr_to_log(hdr); break; } } @@ -261,36 +344,271 @@ int libbootimg_load_headers(struct boot_img_hdr *hdr, return res; } +void libbootimg_get_elf_version(struct boot_img_elf_info *hdr_info) +{ + switch (hdr_info->hdr.phnum) + { + case 3: + // Version 2 (found in Z2, 8960) has 3 program headers + 1 section header + hdr_info->elf_version = VER_ELF_2; + // Also the bootloader does not load modified elf images + // Output to standard Android container format + hdr_info->elf_out_format = OUT_AND; + break; + case 4: + default: + // Version 1 (found in SP, 8960) has 4 program headers + hdr_info->elf_version = VER_ELF_1; + hdr_info->elf_out_format = OUT_ELF; + break; + } +} + +int libbootimg_load_elf_prog_header(struct boot_img_elf_info *hdr_info, FILE *f) +{ + int res = 0; + int prog_entry_size = sizeof(struct boot_img_elf_prog_hdr); + struct boot_img_elf_prog_hdr *prog_entry; + prog_entry = malloc(prog_entry_size * hdr_info->hdr.phnum); + hdr_info->prog = prog_entry; + + int prog_entry_idx = 0; + for (; prog_entry_idx < hdr_info->hdr.phnum; ++prog_entry_idx) + { + LOG_DBG("Reading program header %u/%u.\n", prog_entry_idx + 1, hdr_info->hdr.phnum); + res = fread(&prog_entry[prog_entry_idx], prog_entry_size, 1, f); + if (res == 1) + { + LOG_DBG("Program header %u/%u successfully read.\n", prog_entry_idx + 1, hdr_info->hdr.phnum); + print_elf_prog_hdr_to_log(&hdr_info->prog[prog_entry_idx]); + } + else + { + break; + } + } + return res; +} + +int libbootimg_load_elf_sect_header(struct boot_img_elf_info *hdr_info, FILE *f) +{ + int res = 0; + int sect_entry_size = sizeof(struct boot_img_elf_sect_hdr); + struct boot_img_elf_sect_hdr *sect_entry; + + fseek(f, hdr_info->hdr.shoff, SEEK_SET); + sect_entry = malloc(hdr_info->hdr.shentsize * hdr_info->hdr.shnum); + hdr_info->sect = sect_entry; + + int sect_entry_idx = 0; + for (; sect_entry_idx < hdr_info->hdr.shnum; ++sect_entry_idx) + { + LOG_DBG("Reading section header %u/%u.\n", sect_entry_idx + 1, hdr_info->hdr.shnum); + res = fread(§_entry[sect_entry_idx], sect_entry_size, 1, f); + + if (res == 1) + { + LOG_DBG("Section header %u/%u successfully read.\n", sect_entry_idx + 1, hdr_info->hdr.shnum); + print_elf_sect_hdr_to_log(&hdr_info->sect[sect_entry_idx]); + } + else + { + break; + } + } + + return res; +} + +int libbootimg_load_elf_misc_header(struct boot_img_elf_info *hdr_info, uint32_t page_size, FILE *f) +{ + if (hdr_info->elf_version != VER_ELF_1) + { + return -1; + } + + int res = 0; + int misc_entry_size = sizeof(struct boot_img_elf_misc_hdr); + struct boot_img_elf_misc_hdr *misc; + + int misc_offset = hdr_info->hdr.phoff + hdr_info->hdr.phnum * hdr_info->hdr.phentsize; + + fseek(f, misc_offset, SEEK_SET); + misc = malloc(misc_entry_size); + hdr_info->misc = misc; + + misc->data_size = (page_size - misc_offset - sizeof(misc->name)) * + sizeof(uint8_t); + misc->data = malloc(misc->data_size); + + res = fread(misc->data, misc->data_size, 1, f); + if (res == 1) + { + res = fread(misc->name, sizeof(misc->name), 1, f); + } + + LOG_DBG("Misc data size: %u / %u\n", misc->data_size, page_size); + LOG_DBG("Misc boot name: %s\n", misc->name); + + return res; +} + +void libbootimg_read_cmdline(struct boot_img_hdr *hdr, struct boot_img_elf_info *elf_info, FILE *f) +{ + unsigned char buf[BOOT_ARGS_SIZE]; + int cmdline_start_pos = 0; + int cmd_len_max = 0; + + if (elf_info->elf_version == VER_ELF_1) + { + cmdline_start_pos = elf_info->prog[ELF_PROG_CMD].offset; + elf_info->cmdline_size = elf_info->prog[ELF_PROG_CMD].size; + cmd_len_max = elf_info->cmdline_size; + } + else if (elf_info->elf_version == VER_ELF_2) + { + cmdline_start_pos = elf_info->sect[ELF_SECT_CMD].offset; + elf_info->cmdline_size = elf_info->sect[ELF_SECT_CMD].size; + cmd_len_max = elf_info->cmdline_size; + } + cmd_len_max = cmd_len_max < BOOT_ARGS_SIZE ? cmd_len_max : BOOT_ARGS_SIZE; + + memset(&hdr->cmdline, '\0', BOOT_ARGS_SIZE); + memset(&buf, '\0', BOOT_ARGS_SIZE); + fseek(f, cmdline_start_pos, SEEK_SET); + fread(buf, cmd_len_max, 1, f); + + int buf_offset = 0; + if (elf_info->elf_version == VER_ELF_2) + { + buf_offset = 8; + } + + int buf_idx = 0; + for (; buf_idx < cmd_len_max; ++buf_idx) + { + hdr->cmdline[buf_idx] = buf[buf_idx + buf_offset]; + if (buf[buf_idx + buf_offset] == '\0') + { + break; + } + } + LOG_DBG("Cmd line: %s\n", hdr->cmdline); +} + +struct boot_img_elf_prog_hdr* get_elf_proc_hdr_of(struct boot_img_elf_info *elf_info, int type) +{ + switch (type) + { + case LIBBOOTIMG_BLOB_KERNEL: + return &elf_info->prog[ELF_PROG_KER]; + case LIBBOOTIMG_BLOB_RAMDISK: + return &elf_info->prog[ELF_PROG_RAM]; + case LIBBOOTIMG_BLOB_SECOND: + if (elf_info->elf_version == VER_ELF_1) + { + return &elf_info->prog[ELF_PROG_RPM]; + } + break; + case LIBBOOTIMG_BLOB_DTB: + if (elf_info->elf_version == VER_ELF_2) + { + return &elf_info->prog[ELF_PROG_RPM]; + } + break; + default: + break; + } + return NULL; +} + int libbootimg_update_headers(struct bootimg *b) { - uint32_t addr; + uint32_t addr = 0; if (b == NULL) return translate_errnum(ENOENT); - if (b->is_elf) + if (b->is_elf && b->hdr_info->elf_out_format == OUT_ELF) { - b->hdr_elf->kernel_size = b->hdr.kernel_size; - b->hdr_elf->kernel_msize = b->hdr.kernel_size; - b->hdr_elf->ramdisk_size = b->hdr.ramdisk_size; - b->hdr_elf->ramdisk_msize = b->hdr.ramdisk_size; - b->hdr_elf->rpm_size = b->hdr.second_size; - b->hdr_elf->rpm_msize = b->hdr.second_size; + b->hdr_info->prog[ELF_PROG_KER].size = b->hdr.kernel_size; + b->hdr_info->prog[ELF_PROG_KER].msize = b->hdr.kernel_size; + b->hdr_info->prog[ELF_PROG_RAM].size = b->hdr.ramdisk_size; + b->hdr_info->prog[ELF_PROG_RAM].msize = b->hdr.ramdisk_size; + b->hdr_info->prog[ELF_PROG_RPM].size = b->hdr.second_size; + b->hdr_info->prog[ELF_PROG_RPM].msize = b->hdr.second_size; + + if (b->hdr_info->elf_version == VER_ELF_1) + { + b->hdr_info->prog[ELF_PROG_CMD].size = b->hdr_info->cmdline_size; + b->hdr_info->prog[ELF_PROG_CMD].msize = b->hdr_info->cmdline_size; + } + else + { + b->hdr_info->prog[ELF_PROG_CMD].size = b->hdr.dt_size; + b->hdr_info->prog[ELF_PROG_CMD].msize = b->hdr.dt_size; + } + + LOG_DBG("Updating program headers...\n"); - addr = b->hdr_elf->kernel_offset; + addr = b->hdr_info->prog[ELF_PROG_KER].offset; - addr += b->hdr_elf->kernel_size; - b->hdr_elf->ramdisk_offset = addr; + addr += b->hdr_info->prog[ELF_PROG_KER].size; + b->hdr_info->prog[ELF_PROG_RAM].offset = addr; - addr += b->hdr_elf->ramdisk_size; - b->hdr_elf->rpm_offset = addr; + addr += b->hdr_info->prog[ELF_PROG_RAM].size; + b->hdr_info->prog[ELF_PROG_RPM].offset = addr; - addr += b->hdr_elf->rpm_size; - b->hdr_elf->cmd_offset = addr; + addr += b->hdr_info->prog[ELF_PROG_RPM].size; + b->hdr_info->prog[ELF_PROG_CMD].offset = addr; - memcpy(b->hdr_elf->name, b->hdr.name, BOOT_NAME_SIZE); + addr += b->hdr_info->prog[ELF_PROG_CMD].size; + + // Also, we need to update the address/offset pointers & sizes + // in the section header table + int sect_entry_idx = 0; + for (; sect_entry_idx < b->hdr_info->hdr.shnum; ++sect_entry_idx) + { + LOG_DBG("Updating section header entry %d/%d...\n", + sect_entry_idx + 1, b->hdr_info->hdr.shnum); + b->hdr_info->sect[sect_entry_idx].offset = addr; + addr += b->hdr_info->sect[sect_entry_idx].size; + } + + if (b->hdr_info->elf_version == VER_ELF_1) + { + LOG_DBG("Updating misc header: name %s...\n", b->hdr.name); + memcpy(b->hdr_info->misc->name, b->hdr.name, BOOT_NAME_SIZE); + } + + // The section header is placed after the last section + if (b->hdr_info->hdr.shnum > 0) + { + b->hdr_info->hdr.shoff = addr; + } + + } + else if (b->is_elf && b->hdr_info->elf_out_format == OUT_AND) + { + // ELF format found in stock Sony ROMs for, e.g, the Xperia Z2. + // If more formats exist, we should differ the assignments at + // the bottom of this function by the "elf version" + + b->hdr.magic[0] = 'A'; + b->hdr.magic[1] = 'N'; + b->hdr.magic[2] = 'D'; + b->hdr.magic[3] = 'R'; + b->hdr.magic[4] = 'O'; + b->hdr.magic[5] = 'I'; + b->hdr.magic[6] = 'D'; + b->hdr.magic[7] = '!'; + + b->hdr.kernel_addr = b->hdr_info->prog[ELF_PROG_KER].paddr; + b->hdr.ramdisk_addr = b->hdr_info->prog[ELF_PROG_RAM].paddr; + b->hdr.tags_addr = b->hdr_info->prog[ELF_PROG_RPM].paddr; } + print_hdr_to_log(&b->hdr); return 0; } @@ -402,12 +720,6 @@ int libbootimg_write_img(struct bootimg *b, const char *dest) FILE *f; int res; - if(b->hdr.kernel_size == 0 || b->hdr.ramdisk_size == 0) - return LIBBOOTIMG_ERROR_MISSING_BLOB; - - if(b->hdr.page_size < sizeof(b->hdr)) - return LIBBOOTIMG_ERROR_INVALID_PAGESIZE; - f = fopen(dest, "w"); if(!f) return translate_errnum(errno); @@ -423,10 +735,12 @@ int libbootimg_write_img_fileptr(struct bootimg *b, FILE *f) int i; int res = 0; char *blank = NULL; - size_t padding; + size_t padding = 0; struct bootimg_blob *blob; - int pos_start, pos_end; + int pos_start; + int pos_end = 0; + LOG_DBG("Writing.\n"); pos_start = ftell(f); if(pos_start < 0) return translate_errnum(errno); @@ -449,47 +763,139 @@ int libbootimg_write_img_fileptr(struct bootimg *b, FILE *f) blank = malloc(b->hdr.page_size); memset(blank, 0, b->hdr.page_size); - // write header - if (!b->is_elf) + // Update & write header + libbootimg_update_headers(b); + if (!b->is_elf || b->hdr_info->elf_out_format == OUT_AND) { if (fwrite(&b->hdr, sizeof(b->hdr), 1, f) != 1) goto fail_fwrite; padding = align_size(sizeof(b->hdr), b->hdr.page_size) - sizeof(b->hdr); } - else + else if (b->is_elf && b->hdr_info->elf_out_format == OUT_ELF) { - libbootimg_update_headers(b); - - if (fwrite(b->hdr_elf, sizeof(*b->hdr_elf), 1, f) != 1) + if (fwrite(&b->hdr_info->hdr, sizeof(b->hdr_info->hdr), 1, f) != 1) + { goto fail_fwrite; + } + + // Write the Program headers. + int num_prog_hdr = b->hdr_info->hdr.phnum; + int hdr_entry_idx = 0; + for (; hdr_entry_idx < num_prog_hdr; ++hdr_entry_idx) + { + print_elf_prog_hdr_to_log(&b->hdr_info->prog[hdr_entry_idx]); + if (fwrite(&b->hdr_info->prog[hdr_entry_idx], + b->hdr_info->hdr.phentsize, 1, f) == 1) + { + LOG_DBG("Program header %u/%u writing successful.\n", + hdr_entry_idx + 1, b->hdr_info->hdr.phnum); + } + else + { + LOG_DBG("Program failed %u/%u writing failed.\n", + hdr_entry_idx + 1, b->hdr_info->hdr.phnum); + goto fail_fwrite; + } + } - padding = align_size(sizeof(*b->hdr_elf), b->hdr.page_size) - sizeof(*b->hdr_elf); + if (b->hdr_info->elf_version == VER_ELF_1) + { + print_elf_misc_hdr_to_log(b->hdr_info->misc); + if (fwrite(b->hdr_info->misc->data, b->hdr_info->misc->data_size, + 1, f) != 1) + { + LOG_DBG("Misc data writing successful.\n"); + goto fail_fwrite; + } + else if (fwrite(b->hdr_info->misc->name, + sizeof(b->hdr_info->misc->name), 1, f) != 1) + { + LOG_DBG("Misc boot name writing failed.\n"); + goto fail_fwrite; + } + else + { + LOG_DBG("Misc header writing successful.\n"); + } + } + else if (b->hdr_info->elf_version == VER_ELF_2) + { + // Fill remaining block with 0s up to first prog entry + // (misc data NOT written back!) + uint32_t header_size = sizeof(b->hdr_info->hdr) + + b->hdr_info->hdr.phnum * b->hdr_info->hdr.phentsize + + b->hdr_info->hdr.shnum * b->hdr_info->hdr.shentsize; + uint32_t blank_size = b->hdr.page_size - header_size; + if (fwrite(blank, blank_size, 1, f) != blank_size) + { + goto fail_fwrite; + } + } } - if (fwrite(blank, 1, padding, f) != padding) - goto fail_fwrite; + if (!b->is_elf || b->hdr_info->elf_out_format == OUT_AND) + { + if (fwrite(blank, 1, padding, f) != padding) + goto fail_fwrite; + } for (i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i) { + LOG_DBG("Writing blob number %d/%d...\n", i + 1, LIBBOOTIMG_BLOB_CNT); + LOG_DBG("Writing to position 0x%lx.\n", ftell(f)); blob = &b->blobs[i]; if (*blob->size == 0) continue; if (fwrite(blob->data, *blob->size, 1, f) != 1) + { + LOG_DBG("Writing blob %d failed.\n", i + 1); goto fail_fwrite; + } - if (!b->is_elf) + if (!b->is_elf || b->hdr_info->elf_out_format == OUT_AND) { padding = align_size(*blob->size, b->hdr.page_size) - *blob->size; if (fwrite(blank, 1, padding, f) != padding) + { + LOG_DBG("Writing padding of blob %d failed.\n", i + 1); goto fail_fwrite; + } } } - if (b->is_elf && fwrite(b->hdr.cmdline, b->hdr_elf->cmd_size, 1, f) != 1) - goto fail_fwrite; + if (b->is_elf && b->hdr_info->elf_out_format == OUT_ELF) + { + if (b->hdr_info->elf_version == VER_ELF_1) + { + pos_end += b->hdr_info->cmdline_size; + LOG_DBG("cmdline: %s\n", b->hdr.cmdline); + LOG_DBG("cmdline: %x\n", b->hdr_info->cmdline_size); + // Write the cmdline (last part ref by the prog header) + if (fwrite(&b->hdr.cmdline, b->hdr_info->cmdline_size, 1, f) != 1) + { + LOG_DBG("Failed to write the cmdline.\n"); + goto fail_fwrite; + } + } + + // Write the section header if needed by the ELF + if (b->hdr_info->hdr.shnum > 0) + { + LOG_DBG("Writing section header.\n"); + fseek(f, b->hdr_info->hdr.shoff, SEEK_SET); + if (fwrite(b->hdr_info->sect, b->hdr_info->hdr.shentsize, 1, f) == 1) + { + print_elf_sect_hdr_to_log(&b->hdr_info->sect[ELF_SECT_CMD]); + } + else + { + goto fail_fwrite; + } + } + } pos_end = ftell(f); @@ -525,7 +931,7 @@ const char *libbootimg_version_str(void) const char *libbootimg_error_str(int error) { - switch(error) + switch (error) { case LIBBOOTIMG_SUCCESS: return "No errors"; case LIBBOOTIMG_ERROR_IO: return "Input/output error"; @@ -542,3 +948,86 @@ const char *libbootimg_error_str(int error) default: return "Unknown error"; } } + +void print_hdr_to_log(struct boot_img_hdr* hdr) +{ + LOG_DBG("* kernel size = %u bytes (%.2f MB)\n", hdr->kernel_size, + (double )hdr->kernel_size / 0x100000); + LOG_DBG(" ramdisk size = %u bytes (%.2f MB)\n", hdr->ramdisk_size, + (double )hdr->ramdisk_size / 0x100000); + if (hdr->second_size) + { + LOG_DBG(" second stage size = %u bytes (%.2f MB)\n", hdr->second_size, + (double )hdr->second_size / 0x100000); + } + if (hdr->dt_size) + { + LOG_DBG(" device tree size = %u bytes (%.2f MB)\n", hdr->dt_size, + (double )hdr->dt_size / 0x100000); + } + LOG_DBG("* load addresses:\n"); + LOG_DBG(" kernel: 0x%08x\n", hdr->kernel_addr); + LOG_DBG(" ramdisk: 0x%08x\n", hdr->ramdisk_addr); + if (hdr->second_size) + { + LOG_DBG(" second stage: 0x%08x\n", hdr->second_addr); + } + LOG_DBG(" tags: 0x%08x\n", hdr->tags_addr); +} + +void print_elf_hdr_to_log(struct boot_img_elf_info* elf_info) +{ + (void)elf_info; + LOG_DBG("========= ELF header content =========\n"); + LOG_DBG("Type (0x10 to 0x11) = %x\n", elf_info->hdr.type); + LOG_DBG("Machine (0x12 to 0x13) = %x\n", elf_info->hdr.machine); + LOG_DBG("Version (0x14 to 0x17) = %x\n", elf_info->hdr.version); + LOG_DBG("Entry Address (0x18 to 0x1B) = %x\n", elf_info->hdr.entry_addr); + LOG_DBG("Phys Offset (0x1C to 0x1F) = %x\n", elf_info->hdr.phoff); + LOG_DBG("Section Offset (0x20 to 0x23) = %x\n", elf_info->hdr.shoff); + LOG_DBG("Flags (0x24 to 0x27) = %x\n", elf_info->hdr.flags); + LOG_DBG("Ehsize (0x28 to 0x29) = %x\n", elf_info->hdr.ehsize); + LOG_DBG("Ph entry size (0x2A to 0x2B) = %x\n", elf_info->hdr.phentsize); + LOG_DBG("Ph number (0x2C to 0x2D) = %x\n", elf_info->hdr.phnum); + LOG_DBG("Sect entry size (0x2E to 0x2F) = %x\n", elf_info->hdr.shentsize); + LOG_DBG("Sect number (0x30 to 0x31) = %x\n", elf_info->hdr.shnum); + LOG_DBG("Sect strndx (0x32 to 0x33) = %x\n", elf_info->hdr.shstrndx); + LOG_DBG("======================================\n"); +} + +void print_elf_prog_hdr_to_log(struct boot_img_elf_prog_hdr* elf_prog_hdr) +{ + (void)elf_prog_hdr; + LOG_DBG("===== ELF program header content =====\n"); + LOG_DBG("Type = %x\n", elf_prog_hdr->type); + LOG_DBG("Offset = %x\n", elf_prog_hdr->offset); + LOG_DBG("VAddr = %x\n", elf_prog_hdr->vaddr); + LOG_DBG("PAddr = %x\n", elf_prog_hdr->paddr); + LOG_DBG("Size = %x\n", elf_prog_hdr->size); + LOG_DBG("MSize = %x\n", elf_prog_hdr->msize); + LOG_DBG("Flags = %x\n", elf_prog_hdr->flags); + LOG_DBG("Align = %x\n", elf_prog_hdr->align); + LOG_DBG("======================================\n"); +} + +void print_elf_sect_hdr_to_log(struct boot_img_elf_sect_hdr* elf_sect_hdr) +{ + (void)elf_sect_hdr; + LOG_DBG("===== ELF section header content =====\n"); + LOG_DBG("Name = %x\n", elf_sect_hdr->name); + LOG_DBG("Type = %x\n", elf_sect_hdr->type); + LOG_DBG("Flags = %x\n", elf_sect_hdr->flags); + LOG_DBG("Address = %x\n", elf_sect_hdr->addr); + LOG_DBG("Offset = %x\n", elf_sect_hdr->offset); + LOG_DBG("Size = %x\n", elf_sect_hdr->size); + LOG_DBG("======================================\n"); +} + +void print_elf_misc_hdr_to_log(struct boot_img_elf_misc_hdr* elf_misc_hdr) +{ + (void)elf_misc_hdr; + LOG_DBG("====== ELF misc header content =======\n"); + LOG_DBG("Data size = %u\n", elf_misc_hdr->data_size); + LOG_DBG("Boot name = %s\n", elf_misc_hdr->name); + LOG_DBG("======================================\n"); +}