From fb3204d69c51c44c3bba67027ee7c8295fd4f985 Mon Sep 17 00:00:00 2001 From: Sergi Granell Date: Tue, 8 Feb 2022 19:05:23 +0100 Subject: [PATCH] elf2nro, elf2nso: Add ARM support (32-bit) --- src/elf2nro.c | 125 +++++++++++++++++++++++++++++++++++++++----------- src/elf2nso.c | 124 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 196 insertions(+), 53 deletions(-) diff --git a/src/elf2nro.c b/src/elf2nro.c index 7f0bc0d..4b3f7d2 100644 --- a/src/elf2nro.c +++ b/src/elf2nro.c @@ -119,20 +119,43 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } - Elf64_Ehdr* hdr = (Elf64_Ehdr*) elf; - if (hdr->e_machine != EM_AARCH64) { - fprintf(stderr, "Invalid ELF: expected AArch64!\n"); + Elf64_Ehdr* hdr64 = (Elf64_Ehdr*) elf; + Elf32_Ehdr* hdr32 = (Elf32_Ehdr*) elf; + Elf64_Half e_phnum, e_shnum; + Elf64_Off e_phoff, e_shoff; + Elf64_Half e_shentsize; + int elf64; + if (hdr64->e_machine == EM_AARCH64) { + elf64 = 1; + e_phnum = hdr64->e_phnum; + e_shnum = hdr64->e_shnum; + e_phoff = hdr64->e_phoff; + e_shoff = hdr64->e_shoff; + e_shentsize = hdr64->e_shentsize; + } else if (hdr64->e_machine == EM_ARM) { + elf64 = 0; + e_phnum = hdr32->e_phnum; + e_shnum = hdr32->e_shnum; + e_phoff = hdr32->e_phoff; + e_shoff = hdr32->e_shoff; + e_shentsize = hdr32->e_shentsize; + } else { + fprintf(stderr, "Invalid ELF: expected AArch64 or ARM!\n"); return EXIT_FAILURE; } - Elf64_Off ph_end = hdr->e_phoff + hdr->e_phnum * sizeof(Elf64_Phdr); + Elf64_Off ph_end; + if (elf64) + ph_end = e_phoff + e_phnum * sizeof(Elf64_Phdr); + else + ph_end = e_phoff + e_phnum * sizeof(Elf32_Phdr); - if (ph_end < hdr->e_phoff || ph_end > elf_len) { + if (ph_end < e_phoff || ph_end > elf_len) { fprintf(stderr, "Invalid ELF: phdrs outside file!\n"); return EXIT_FAILURE; } - Elf64_Phdr* phdrs = (Elf64_Phdr*) &elf[hdr->e_phoff]; + void *phdrs = &elf[e_phoff]; size_t i, j = 0; size_t file_off = 0; size_t tmpsize; @@ -140,11 +163,19 @@ int main(int argc, char* argv[]) { uint8_t* buf[3]; for (i=0; i<4; i++) { - Elf64_Phdr* phdr = NULL; - while (j < hdr->e_phnum) { - Elf64_Phdr* cur = &phdrs[j]; - if (i < 2 || (i==2 && cur->p_type != PT_LOAD)) j++; - if (cur->p_type == PT_LOAD || i == 3) { + void* phdr = NULL; + while (j < e_phnum) { + void *cur; + Elf64_Word p_type; + if (elf64) { + cur = &((Elf64_Phdr*)phdrs)[j]; + p_type = ((Elf64_Phdr*)cur)->p_type; + } else { + cur = &((Elf32_Phdr*)phdrs)[j]; + p_type = ((Elf32_Phdr*)cur)->p_type; + } + if (i < 2 || (i==2 && p_type != PT_LOAD)) j++; + if (p_type == PT_LOAD || i == 3) { phdr = cur; break; } @@ -155,18 +186,33 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } + Elf64_Addr p_vaddr; + Elf64_Xword p_filesz, p_memsz; + Elf64_Off p_offset; + if (elf64) { + p_vaddr = ((Elf64_Phdr*)phdr)->p_vaddr; + p_filesz = ((Elf64_Phdr*)phdr)->p_filesz; + p_memsz = ((Elf64_Phdr*)phdr)->p_memsz; + p_offset = ((Elf64_Phdr*)phdr)->p_offset; + } else { + p_vaddr = ((Elf32_Phdr*)phdr)->p_vaddr; + p_filesz = ((Elf32_Phdr*)phdr)->p_filesz; + p_memsz = ((Elf32_Phdr*)phdr)->p_memsz; + p_offset = ((Elf32_Phdr*)phdr)->p_offset; + } + // .bss is special if (i == 3) { - tmpsize = (phdr->p_filesz + 0xFFF) & ~0xFFF; - if ( phdr->p_memsz > tmpsize) - nro_hdr.bssSize = ((phdr->p_memsz - tmpsize) + 0xFFF) & ~0xFFF; + tmpsize = (p_filesz + 0xFFF) & ~0xFFF; + if (p_memsz > tmpsize) + nro_hdr.bssSize = ((p_memsz - tmpsize) + 0xFFF) & ~0xFFF; else nro_hdr.bssSize = 0; break; } - nro_hdr.Segments[i].FileOff = phdr->p_vaddr; - nro_hdr.Segments[i].Size = (phdr->p_filesz + 0xFFF) & ~0xFFF; + nro_hdr.Segments[i].FileOff = p_vaddr; + nro_hdr.Segments[i].Size = (p_filesz + 0xFFF) & ~0xFFF; buf[i] = malloc(nro_hdr.Segments[i].Size); memset(buf[i], 0, nro_hdr.Segments[i].Size); @@ -175,29 +221,54 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } - memcpy(buf[i], &elf[phdr->p_offset], phdr->p_filesz); + memcpy(buf[i], &elf[p_offset], p_filesz); file_off += nro_hdr.Segments[i].Size; file_off = (file_off + 0xFFF) & ~0xFFF; } /* Iterate over sections to find build id. */ - size_t cur_sect_hdr_ofs = hdr->e_shoff; - for (unsigned int i = 0; i < hdr->e_shnum; i++) { - Elf64_Shdr *cur_shdr = (Elf64_Shdr *)(elf + cur_sect_hdr_ofs); - if (cur_shdr->sh_type == SHT_NOTE) { - Elf64_Nhdr *note_hdr = (Elf64_Nhdr *)(elf + cur_shdr->sh_offset); - u8 *note_name = (u8 *)((uintptr_t)note_hdr + sizeof(Elf64_Nhdr)); - u8 *note_desc = note_name + note_hdr->n_namesz; - if (note_hdr->n_type == NT_GNU_BUILD_ID && note_hdr->n_namesz == 4 && memcmp(note_name, "GNU\x00", 4) == 0) { - size_t build_id_size = note_hdr->n_descsz; + Elf64_Off cur_sect_hdr_ofs = e_shoff; + for (unsigned int i = 0; i < e_shnum; i++) { + Elf64_Word sh_type; + Elf64_Off sh_offset; + void *cur_shdr = elf + cur_sect_hdr_ofs; + if (elf64) { + sh_type = ((Elf64_Shdr *)cur_shdr)->sh_type; + sh_offset = ((Elf64_Shdr *)cur_shdr)->sh_offset; + } else { + sh_type = ((Elf32_Shdr *)cur_shdr)->sh_type; + sh_offset = ((Elf32_Shdr *)cur_shdr)->sh_offset; + } + + if (sh_type == SHT_NOTE) { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; + u8 *note_name; + void *note_hdr = elf + sh_offset; + if (elf64) { + n_namesz = ((Elf64_Nhdr *)note_hdr)->n_namesz; + n_descsz = ((Elf64_Nhdr *)note_hdr)->n_descsz; + n_type = ((Elf64_Nhdr *)note_hdr)->n_type; + note_name = (u8 *)note_hdr + sizeof(Elf64_Nhdr); + } else { + n_namesz = ((Elf32_Nhdr *)note_hdr)->n_namesz; + n_descsz = ((Elf32_Nhdr *)note_hdr)->n_descsz; + n_type = ((Elf32_Nhdr *)note_hdr)->n_type; + note_name = (u8 *)note_hdr + sizeof(Elf32_Nhdr); + } + + u8 *note_desc = note_name + n_namesz; + if (n_type == NT_GNU_BUILD_ID && n_namesz == 4 && memcmp(note_name, "GNU\x00", 4) == 0) { + size_t build_id_size = n_descsz; if (build_id_size > 0x20) { build_id_size = 0x20; } memcpy(nro_hdr.BuildId, note_desc, build_id_size); } } - cur_sect_hdr_ofs += hdr->e_shentsize; + cur_sect_hdr_ofs += e_shentsize; } FILE* out = fopen(argv[2], "wb"); diff --git a/src/elf2nso.c b/src/elf2nso.c index e3c8b83..443f668 100644 --- a/src/elf2nso.c +++ b/src/elf2nso.c @@ -88,20 +88,44 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } - Elf64_Ehdr* hdr = (Elf64_Ehdr*) elf; - if (hdr->e_machine != EM_AARCH64) { - fprintf(stderr, "Invalid ELF: expected AArch64!\n"); + Elf64_Ehdr* hdr64 = (Elf64_Ehdr*) elf; + Elf32_Ehdr* hdr32 = (Elf32_Ehdr*) elf; + Elf64_Half e_phnum, e_shnum; + Elf64_Off e_phoff, e_shoff; + Elf64_Half e_shentsize; + int elf64; + if (hdr64->e_machine == EM_AARCH64) { + elf64 = 1; + e_phnum = hdr64->e_phnum; + e_shnum = hdr64->e_shnum; + e_phoff = hdr64->e_phoff; + e_shoff = hdr64->e_shoff; + e_shentsize = hdr64->e_shentsize; + } else if (hdr64->e_machine == EM_ARM) { + elf64 = 0; + e_phnum = hdr32->e_phnum; + e_shnum = hdr32->e_shnum; + e_phoff = hdr32->e_phoff; + e_shoff = hdr32->e_shoff; + e_shentsize = hdr32->e_shentsize; + } else { + fprintf(stderr, "Invalid ELF: expected AArch64 or ARM!\n"); return EXIT_FAILURE; } - Elf64_Off ph_end = hdr->e_phoff + hdr->e_phnum * sizeof(Elf64_Phdr); + Elf64_Off ph_end; + if (elf64) + ph_end = e_phoff + e_phnum * sizeof(Elf64_Phdr); + else + ph_end = e_phoff + e_phnum * sizeof(Elf32_Phdr); - if (ph_end < hdr->e_phoff || ph_end > elf_len) { + if (ph_end < e_phoff || ph_end > elf_len) { fprintf(stderr, "Invalid ELF: phdrs outside file!\n"); return EXIT_FAILURE; } - Elf64_Phdr* phdrs = (Elf64_Phdr*) &elf[hdr->e_phoff]; + void *phdrs = &elf[e_phoff]; + size_t i, j = 0; size_t file_off = sizeof(NsoHeader); @@ -109,10 +133,18 @@ int main(int argc, char* argv[]) { int comp_sz[3]; for (i=0; i<3; i++) { - Elf64_Phdr* phdr = NULL; - while (j < hdr->e_phnum) { - Elf64_Phdr* cur = &phdrs[j++]; - if (cur->p_type == PT_LOAD) { + void* phdr = NULL; + while (j < e_phnum) { + void *cur; + Elf64_Word p_type; + if (elf64) { + cur = &((Elf64_Phdr*)phdrs)[j++]; + p_type = ((Elf64_Phdr*)cur)->p_type; + } else { + cur = &((Elf32_Phdr*)phdrs)[j++]; + p_type = ((Elf32_Phdr*)cur)->p_type; + } + if (p_type == PT_LOAD) { phdr = cur; break; } @@ -123,22 +155,37 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } + Elf64_Addr p_vaddr; + Elf64_Xword p_filesz, p_memsz; + Elf64_Off p_offset; + if (elf64) { + p_vaddr = ((Elf64_Phdr*)phdr)->p_vaddr; + p_filesz = ((Elf64_Phdr*)phdr)->p_filesz; + p_memsz = ((Elf64_Phdr*)phdr)->p_memsz; + p_offset = ((Elf64_Phdr*)phdr)->p_offset; + } else { + p_vaddr = ((Elf32_Phdr*)phdr)->p_vaddr; + p_filesz = ((Elf32_Phdr*)phdr)->p_filesz; + p_memsz = ((Elf32_Phdr*)phdr)->p_memsz; + p_offset = ((Elf32_Phdr*)phdr)->p_offset; + } + nso_hdr.Segments[i].FileOff = file_off; - nso_hdr.Segments[i].DstOff = phdr->p_vaddr; - nso_hdr.Segments[i].DecompSz = phdr->p_filesz; + nso_hdr.Segments[i].DstOff = p_vaddr; + nso_hdr.Segments[i].DecompSz = p_filesz; // for .data segment this field contains bss size if (i == 2) - nso_hdr.Segments[i].AlignOrTotalSz = phdr->p_memsz - phdr->p_filesz; + nso_hdr.Segments[i].AlignOrTotalSz = p_memsz - p_filesz; else nso_hdr.Segments[i].AlignOrTotalSz = 1; SHA256_CTX ctx; sha256_init(&ctx); - sha256_update(&ctx, &elf[phdr->p_offset], phdr->p_filesz); + sha256_update(&ctx, &elf[p_offset], p_filesz); sha256_final(&ctx, (u8*) &nso_hdr.Hashes[i]); - size_t comp_max = LZ4_compressBound(phdr->p_filesz); + size_t comp_max = LZ4_compressBound(p_filesz); comp_buf[i] = malloc(comp_max); if (comp_buf[i] == NULL) { @@ -147,7 +194,7 @@ int main(int argc, char* argv[]) { } // TODO check p_offset - comp_sz[i] = LZ4_compress_default(&elf[phdr->p_offset], comp_buf[i], phdr->p_filesz, comp_max); + comp_sz[i] = LZ4_compress_default(&elf[p_offset], comp_buf[i], p_filesz, comp_max); if (comp_sz[i] < 0) { fprintf(stderr, "Failed to compress!\n"); @@ -159,22 +206,47 @@ int main(int argc, char* argv[]) { } /* Iterate over sections to find build id. */ - size_t cur_sect_hdr_ofs = hdr->e_shoff; - for (unsigned int i = 0; i < hdr->e_shnum; i++) { - Elf64_Shdr *cur_shdr = (Elf64_Shdr *)(elf + cur_sect_hdr_ofs); - if (cur_shdr->sh_type == SHT_NOTE) { - Elf64_Nhdr *note_hdr = (Elf64_Nhdr *)(elf + cur_shdr->sh_offset); - u8 *note_name = (u8 *)((uintptr_t)note_hdr + sizeof(Elf64_Nhdr)); - u8 *note_desc = note_name + note_hdr->n_namesz; - if (note_hdr->n_type == NT_GNU_BUILD_ID && note_hdr->n_namesz == 4 && memcmp(note_name, "GNU\x00", 4) == 0) { - size_t build_id_size = note_hdr->n_descsz; + Elf64_Off cur_sect_hdr_ofs = e_shoff; + for (unsigned int i = 0; i < e_shnum; i++) { + Elf64_Word sh_type; + Elf64_Off sh_offset; + void *cur_shdr = elf + cur_sect_hdr_ofs; + if (elf64) { + sh_type = ((Elf64_Shdr *)cur_shdr)->sh_type; + sh_offset = ((Elf64_Shdr *)cur_shdr)->sh_offset; + } else { + sh_type = ((Elf32_Shdr *)cur_shdr)->sh_type; + sh_offset = ((Elf32_Shdr *)cur_shdr)->sh_offset; + } + + if (sh_type == SHT_NOTE) { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; + u8 *note_name; + void *note_hdr = elf + sh_offset; + if (elf64) { + n_namesz = ((Elf64_Nhdr *)note_hdr)->n_namesz; + n_descsz = ((Elf64_Nhdr *)note_hdr)->n_descsz; + n_type = ((Elf64_Nhdr *)note_hdr)->n_type; + note_name = (u8 *)note_hdr + sizeof(Elf64_Nhdr); + } else { + n_namesz = ((Elf32_Nhdr *)note_hdr)->n_namesz; + n_descsz = ((Elf32_Nhdr *)note_hdr)->n_descsz; + n_type = ((Elf32_Nhdr *)note_hdr)->n_type; + note_name = (u8 *)note_hdr + sizeof(Elf32_Nhdr); + } + + u8 *note_desc = note_name + n_namesz; + if (n_type == NT_GNU_BUILD_ID && n_namesz == 4 && memcmp(note_name, "GNU\x00", 4) == 0) { + size_t build_id_size = n_descsz; if (build_id_size > 0x20) { build_id_size = 0x20; } memcpy(nso_hdr.BuildId, note_desc, build_id_size); } } - cur_sect_hdr_ofs += hdr->e_shentsize; + cur_sect_hdr_ofs += e_shentsize; } FILE* out = fopen(argv[2], "wb");