[PATCH 5/8] powerpc: Add functions to read ELF files of any endianness.
A little endian kernel might need to kexec a big endian kernel (the opposite is less likely but could happen as well), so we can't just cast the buffer with the binary to ELF structs and use them as is done elsewhere. This patch adds functions which do byte-swapping as necessary when populating the ELF structs. These functions will be used in the next patch in the series. Cc: ke...@lists.infradead.org Cc: linux-kernel@vger.kernel.org --- arch/powerpc/include/asm/elf_util.h | 19 ++ arch/powerpc/kernel/Makefile| 2 +- arch/powerpc/kernel/elf_util.c | 476 3 files changed, 496 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h index 19ff3335d17d..9cd24bc528b1 100644 --- a/arch/powerpc/include/asm/elf_util.h +++ b/arch/powerpc/include/asm/elf_util.h @@ -20,6 +20,14 @@ #include struct elf_info { + /* +* Where the ELF binary contents are kept. +* Memory managed by the user of the struct. +*/ + const char *buffer; + + const struct elfhdr *ehdr; + const struct elf_phdr *proghdrs; struct elf_shdr *sechdrs; /* Index of stubs section. */ @@ -63,6 +71,17 @@ static inline unsigned long my_r2(const struct elf_info *elf_info) return elf_info->sechdrs[elf_info->toc_section].sh_addr + 0x8000; } +static inline bool elf_is_elf_file(const struct elfhdr *ehdr) +{ + return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0; +} + +int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr, +struct elf_info *elf_info); +void elf_init_elf_info(const struct elfhdr *ehdr, struct elf_shdr *sechdrs, + struct elf_info *elf_info); +void elf_free_info(struct elf_info *elf_info); + int elf64_apply_relocate_add(const struct elf_info *elf_info, const char *strtab, const Elf64_Rela *rela, unsigned int num_rela, void *syms_base, diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index e99f626acc85..8a53fccaa053 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -125,7 +125,7 @@ obj-y += iomap.o endif ifeq ($(CONFIG_MODULES)$(CONFIG_WORD_SIZE),y64) -obj-y += elf_util_64.o +obj-y += elf_util.o elf_util_64.o endif obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o diff --git a/arch/powerpc/kernel/elf_util.c b/arch/powerpc/kernel/elf_util.c new file mode 100644 index ..760be6a5c393 --- /dev/null +++ b/arch/powerpc/kernel/elf_util.c @@ -0,0 +1,476 @@ +/* + * Utility functions to work with ELF files. + * + * Copyright (C) 2016, IBM Corporation + * + * Based on kexec-tools' kexec-elf.c. Heavily modified for the + * kernel by Thiago Jung Bauermann. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#if ELF_CLASS == ELFCLASS32 +#define elf_addr_to_cpuelf32_to_cpu + +#ifndef Elf_Rel +#define Elf_RelElf32_Rel +#endif /* Elf_Rel */ +#else /* ELF_CLASS == ELFCLASS32 */ +#define elf_addr_to_cpuelf64_to_cpu + +#ifndef Elf_Rel +#define Elf_RelElf64_Rel +#endif /* Elf_Rel */ + +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le64_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be64_to_cpu(value); + + return value; +} +#endif /* ELF_CLASS == ELFCLASS32 */ + +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le16_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be16_to_cpu(value); + + return value; +} + +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le32_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be32_to_cpu(value); + + return value; +} + +/** + * elf_is_ehdr_sane - check that it is safe to use the ELF header + * @buf_len: size of the buffer in which the ELF file is loaded. + */ +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len) +{ + if
[PATCH 5/8] powerpc: Add functions to read ELF files of any endianness.
A little endian kernel might need to kexec a big endian kernel (the opposite is less likely but could happen as well), so we can't just cast the buffer with the binary to ELF structs and use them as is done elsewhere. This patch adds functions which do byte-swapping as necessary when populating the ELF structs. These functions will be used in the next patch in the series. Cc: ke...@lists.infradead.org Cc: linux-kernel@vger.kernel.org --- arch/powerpc/include/asm/elf_util.h | 19 ++ arch/powerpc/kernel/Makefile| 2 +- arch/powerpc/kernel/elf_util.c | 476 3 files changed, 496 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h index 19ff3335d17d..9cd24bc528b1 100644 --- a/arch/powerpc/include/asm/elf_util.h +++ b/arch/powerpc/include/asm/elf_util.h @@ -20,6 +20,14 @@ #include struct elf_info { + /* +* Where the ELF binary contents are kept. +* Memory managed by the user of the struct. +*/ + const char *buffer; + + const struct elfhdr *ehdr; + const struct elf_phdr *proghdrs; struct elf_shdr *sechdrs; /* Index of stubs section. */ @@ -63,6 +71,17 @@ static inline unsigned long my_r2(const struct elf_info *elf_info) return elf_info->sechdrs[elf_info->toc_section].sh_addr + 0x8000; } +static inline bool elf_is_elf_file(const struct elfhdr *ehdr) +{ + return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0; +} + +int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr, +struct elf_info *elf_info); +void elf_init_elf_info(const struct elfhdr *ehdr, struct elf_shdr *sechdrs, + struct elf_info *elf_info); +void elf_free_info(struct elf_info *elf_info); + int elf64_apply_relocate_add(const struct elf_info *elf_info, const char *strtab, const Elf64_Rela *rela, unsigned int num_rela, void *syms_base, diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index e99f626acc85..8a53fccaa053 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -125,7 +125,7 @@ obj-y += iomap.o endif ifeq ($(CONFIG_MODULES)$(CONFIG_WORD_SIZE),y64) -obj-y += elf_util_64.o +obj-y += elf_util.o elf_util_64.o endif obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o diff --git a/arch/powerpc/kernel/elf_util.c b/arch/powerpc/kernel/elf_util.c new file mode 100644 index ..760be6a5c393 --- /dev/null +++ b/arch/powerpc/kernel/elf_util.c @@ -0,0 +1,476 @@ +/* + * Utility functions to work with ELF files. + * + * Copyright (C) 2016, IBM Corporation + * + * Based on kexec-tools' kexec-elf.c. Heavily modified for the + * kernel by Thiago Jung Bauermann . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#if ELF_CLASS == ELFCLASS32 +#define elf_addr_to_cpuelf32_to_cpu + +#ifndef Elf_Rel +#define Elf_RelElf32_Rel +#endif /* Elf_Rel */ +#else /* ELF_CLASS == ELFCLASS32 */ +#define elf_addr_to_cpuelf64_to_cpu + +#ifndef Elf_Rel +#define Elf_RelElf64_Rel +#endif /* Elf_Rel */ + +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le64_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be64_to_cpu(value); + + return value; +} +#endif /* ELF_CLASS == ELFCLASS32 */ + +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le16_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be16_to_cpu(value); + + return value; +} + +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le32_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be32_to_cpu(value); + + return value; +} + +/** + * elf_is_ehdr_sane - check that it is safe to use the ELF header + * @buf_len: size of the buffer in which the ELF file is loaded. + */ +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len) +{ + if (ehdr->e_phnum > 0 &&