elf2nro, elf2nso: Add ARM support (32-bit)

This commit is contained in:
Sergi Granell 2022-02-08 19:05:23 +01:00
parent 8243e86c15
commit fb3204d69c
2 changed files with 196 additions and 53 deletions

View File

@ -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");

View File

@ -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");