added 1 changeset to branch 'refs/remotes/xyzzy-github/x86_64' old head: f6a3444449c452a63e0e0d91e8480f39bca81aed new head: 8846189866301c85683151a54ec88e809333ca7a ---------------------------------------------------------------------------- 8846189: Handle 64-bit load addresses for ELF64 images in the bootloader. The ELF loader now uses a new platform function, platform_allocate_elf_region, which returns 2 addresses: the real load address and an address where the region is mapped in the loader's address space. All of the ELF loading code has been changed to access the load region through the mapped address rather than the addresses contained in the ELF image. The ELF64 version of platform_allocate_elf_region on x86 uses the existing MMU code, which maps everything at 0x80000000, but returns the correct 64-bit address. The long mode switch code will just set up the 64-bit address space with everything remapped at the correct address. [ Alex Smith <alex@xxxxxxxxxxxxxxxx> ] ---------------------------------------------------------------------------- Commit: 8846189866301c85683151a54ec88e809333ca7a Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Sun Jun 24 21:57:48 2012 UTC ---------------------------------------------------------------------------- 13 files changed, 243 insertions(+), 40 deletions(-) headers/private/kernel/boot/elf.h | 22 ++++- headers/private/kernel/boot/platform.h | 7 ++ src/system/boot/arch/x86_64/Jamfile | 10 +- src/system/boot/loader/elf.cpp | 81 +++++++++++----- src/system/boot/platform/amiga_m68k/mmu.cpp | 14 +++ src/system/boot/platform/atari_m68k/mmu.cpp | 14 +++ src/system/boot/platform/bios_ia32/mmu.cpp | 37 +++++++ src/system/boot/platform/cfe/mmu.cpp | 18 ++++ src/system/boot/platform/openfirmware/mmu.cpp | 18 ++++ src/system/boot/platform/raspberrypi_arm/mmu.cpp | 14 +++ .../boot/platform/routerboard_mipsel/mmu.cpp | 9 ++ src/system/boot/platform/u-boot/mmu.cpp | 18 ++++ src/system/kernel/arch/x86_64/arch_elf.cpp | 21 ++-- ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/boot/elf.h b/headers/private/kernel/boot/elf.h index 88514d9..46bbfc1 100644 --- a/headers/private/kernel/boot/elf.h +++ b/headers/private/kernel/boot/elf.h @@ -1,7 +1,7 @@ /* * Copyright 2003-2004, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights reserved. * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx - * Distributed under the terms of the OpenBeOS License. + * Distributed under the terms of the MIT License. */ #ifndef KERNEL_BOOT_ELF_H #define KERNEL_BOOT_ELF_H @@ -44,9 +44,10 @@ struct preloaded_image { } _PACKED; struct preloaded_elf32_image : public preloaded_image { - Elf32_Ehdr elf_header; + Elf32_Ehdr elf_header; elf32_region text_region; elf32_region data_region; + uint32 mapped_delta; FixedWidthPointer<Elf32_Sym> syms; FixedWidthPointer<Elf32_Rel> rel; @@ -61,9 +62,10 @@ struct preloaded_elf32_image : public preloaded_image { } _PACKED; struct preloaded_elf64_image : public preloaded_image { - Elf64_Ehdr elf_header; + Elf64_Ehdr elf_header; elf64_region text_region; elf64_region data_region; + uint64 mapped_delta; FixedWidthPointer<Elf64_Sym> syms; FixedWidthPointer<Elf64_Rel> rel; @@ -77,11 +79,23 @@ struct preloaded_elf64_image : public preloaded_image { FixedWidthPointer<Elf64_Sym> debug_symbols; } _PACKED; - +#ifdef _BOOT_MODE extern status_t boot_elf_resolve_symbol(preloaded_elf32_image* image, struct Elf32_Sym* symbol, Elf32_Addr* symbolAddress); extern status_t boot_elf_resolve_symbol(preloaded_elf64_image* image, struct Elf64_Sym* symbol, Elf64_Addr* symbolAddress); +// Helper method to set a relocation at the mapped address in the loader's +// address space. +template<typename ImageType, typename AddrType> +inline void +boot_elf_set_relocation(ImageType* image, AddrType resolveAddress, + AddrType finalAddress) +{ + AddrType* dest = (AddrType*)(addr_t)(resolveAddress + image->mapped_delta); + *dest = finalAddress; +} +#endif + #endif /* KERNEL_BOOT_ELF_H */ diff --git a/headers/private/kernel/boot/platform.h b/headers/private/kernel/boot/platform.h index 9f3c8eb..c251488 100644 --- a/headers/private/kernel/boot/platform.h +++ b/headers/private/kernel/boot/platform.h @@ -72,6 +72,13 @@ extern size_t platform_get_user_input_text(Menu *menu, MenuItem *item, char *buffer, size_t bufferSize); extern char* platform_debug_get_log_buffer(size_t* _size); +/* ELF functions */ + +extern status_t platform_allocate_elf_region(uint32 *_address, uint32 size, + uint8 protection, void **_mappedAddress); +extern status_t platform_allocate_elf_region(uint64 *_address, uint64 size, + uint8 protection, void **_mappedAddress); + #endif #endif /* KERNEL_BOOT_PLATFORM_H */ diff --git a/src/system/boot/arch/x86_64/Jamfile b/src/system/boot/arch/x86_64/Jamfile index 3dd0418..f81c3f1 100644 --- a/src/system/boot/arch/x86_64/Jamfile +++ b/src/system/boot/arch/x86_64/Jamfile @@ -2,6 +2,10 @@ SubDir HAIKU_TOP src system boot arch x86_64 ; DEFINES += _BOOT_MODE ; +local bootArchSources = + arch_elf64.cpp +; + local kernelArchSources = arch_elf.cpp cpuid.S @@ -16,18 +20,18 @@ local librootOsArchSources = ; BootMergeObject boot_arch_$(TARGET_ARCH).o : - arch_elf64.cpp + $(bootArchSources) $(kernelArchSources) $(kernelLibArchSources) $(librootOsArchSources) : # additional flags ; +SEARCH on [ FGristFiles $(bootArchSources) ] + = [ FDirName $(HAIKU_TOP) src system boot arch x86 ] ; SEARCH on [ FGristFiles $(kernelArchSources) ] = [ FDirName $(HAIKU_TOP) src system kernel arch x86 ] ; SEARCH on [ FGristFiles $(kernelLibArchSources) ] = [ FDirName $(HAIKU_TOP) src system kernel lib arch x86 ] ; SEARCH on [ FGristFiles $(librootOsArchSources) ] = [ FDirName $(HAIKU_TOP) src system libroot os arch x86 ] ; -SEARCH on [ FGristFiles $(kernelArchSources) ] - = [ FDirName $(HAIKU_TOP) src system boot arch x86 ] ; diff --git a/src/system/boot/loader/elf.cpp b/src/system/boot/loader/elf.cpp index 1e1d7a4..cf780c3 100644 --- a/src/system/boot/loader/elf.cpp +++ b/src/system/boot/loader/elf.cpp @@ -38,6 +38,7 @@ class ELFLoader { private: typedef typename Class::ImageType ImageType; typedef typename Class::RegionType RegionType; + typedef typename Class::AddrType AddrType; typedef typename Class::EhdrType EhdrType; typedef typename Class::PhdrType PhdrType; typedef typename Class::ShdrType ShdrType; @@ -53,7 +54,7 @@ public: private: static status_t _LoadSymbolTable(int fd, ImageType* image); - static status_t _ParseDynamicSection(ImageType* image); + static status_t _ParseDynamicSection(ImageType* image, AddrType delta); }; @@ -62,6 +63,7 @@ struct ELF32Class { typedef preloaded_elf32_image ImageType; typedef elf32_region RegionType; + typedef Elf32_Addr AddrType; typedef Elf32_Ehdr EhdrType; typedef Elf32_Phdr PhdrType; typedef Elf32_Shdr ShdrType; @@ -80,6 +82,7 @@ struct ELF64Class { typedef preloaded_elf64_image ImageType; typedef elf64_region RegionType; + typedef Elf64_Addr AddrType; typedef Elf64_Ehdr EhdrType; typedef Elf64_Phdr PhdrType; typedef Elf64_Shdr ShdrType; @@ -132,6 +135,7 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) size_t totalSize; ssize_t length; status_t status; + void* mappedRegion = NULL; ImageType* image = static_cast<ImageType*>(_image); EhdrType& elfHeader = image->elf_header; @@ -196,8 +200,9 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) B_PAGE_SIZE); region->delta = -region->start; - TRACE(("segment %ld: start = 0x%lx, size = %lu, delta = %lx\n", i, - region->start, region->size, region->delta)); + TRACE(("segment %ld: start = 0x%llx, size = %llu, delta = %llx\n", i, + (uint64)region->start, (uint64)region->size, + (int64)(AddrType)region->delta)); } // found both, text and data? @@ -228,10 +233,10 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) } // The kernel and the modules are relocatable, thus - // platform_allocate_region() can automatically allocate an address, + // platform_allocate_elf_region() can automatically allocate an address, // but shall prefer the specified base address. - if (platform_allocate_region((void **)&firstRegion->start, totalSize, - B_READ_AREA | B_WRITE_AREA, false) < B_OK) { + if (platform_allocate_elf_region(&firstRegion->start, totalSize, + B_READ_AREA | B_WRITE_AREA, &mappedRegion) < B_OK) { status = B_NO_MEMORY; goto error1; } @@ -242,6 +247,16 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) image->data_region.delta += image->data_region.start; image->text_region.delta += image->text_region.start; + TRACE(("text: start 0x%llx, size 0x%llx, delta 0x%llx\n", + (uint64)image->text_region.start, (uint64)image->text_region.size, + (int64)(AddrType)image->text_region.delta)); + TRACE(("data: start 0x%llx, size 0x%llx, delta 0x%llx\n", + (uint64)image->data_region.start, (uint64)image->data_region.size, + (int64)(AddrType)image->data_region.delta)); + + // Calculate the delta from a real load address to the mapped address + image->mapped_delta = (AddrType)(addr_t)mappedRegion - firstRegion->start; + // load program data for (int32 i = 0; i < elfHeader.e_phnum; i++) { @@ -258,10 +273,14 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) else continue; - TRACE(("load segment %d (%ld bytes)...\n", i, header.p_filesz)); + // Calculate where to load the data to. + addr_t dest = region->start + image->mapped_delta; + + TRACE(("load segment %ld (%llu bytes) mapped at 0x%lx...\n", i, + (uint64)header.p_filesz, dest)); length = read_pos(fd, header.p_offset, - (void*)(region->start + (header.p_vaddr % B_PAGE_SIZE)), + (void*)(dest + (header.p_vaddr % B_PAGE_SIZE)), header.p_filesz); if (length < (ssize_t)header.p_filesz) { status = B_BAD_DATA; @@ -274,7 +293,7 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz; if (offset < region->size) - memset((void*)(region->start + offset), 0, region->size - offset); + memset((void*)(dest + offset), 0, region->size - offset); } // offset dynamic section, and program entry addresses by the delta of the @@ -294,8 +313,8 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) return B_OK; error2: - if (image->text_region.start != 0) - platform_free_region((void*)image->text_region.start, totalSize); + if (mappedRegion != NULL) + platform_free_region(mappedRegion, totalSize); error1: free(programHeaders); kernel_args_free(image); @@ -310,13 +329,16 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) { ImageType* image = static_cast<ImageType*>(_image); - status_t status = _ParseDynamicSection(image); + // Pull information out of the dynamic section. First pass through we set + // the addresses we want in our address space. + status_t status = _ParseDynamicSection(image, image->mapped_delta); if (status != B_OK) return status; // deal with the rels first if (image->rel) { - TRACE(("total %i relocs\n", image->rel_len / (int)sizeof(RelType))); + TRACE(("total %i relocs\n", + (int)image->rel_len / (int)sizeof(RelType))); status = boot_arch_elf_relocate_rel(image, image->rel, image->rel_len); if (status < B_OK) @@ -324,15 +346,18 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) } if (image->pltrel) { - TRACE(("total %i plt-relocs\n", - image->pltrel_len / (int)sizeof(RelType))); - RelType* pltrel = image->pltrel; if (image->pltrel_type == DT_REL) { + TRACE(("total %i plt-relocs\n", + (int)image->pltrel_len / (int)sizeof(RelType))); + status = boot_arch_elf_relocate_rel(image, pltrel, image->pltrel_len); } else { - status = boot_arch_elf_relocate_rela(image, (RelaType *)pltrel, + TRACE(("total %i plt-relocs\n", + (int)image->pltrel_len / (int)sizeof(RelaType))); + + status = boot_arch_elf_relocate_rela(image, (RelaType*)pltrel, image->pltrel_len); } if (status < B_OK) @@ -341,13 +366,17 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) if (image->rela) { TRACE(("total %i rela relocs\n", - image->rela_len / (int)sizeof(RelaType))); + (int)image->rela_len / (int)sizeof(RelaType))); status = boot_arch_elf_relocate_rela(image, image->rela, image->rela_len); if (status < B_OK) return status; } + // Make a second pass through the dynamic section, storing the correct + // virtual addresses for the kernel. + _ParseDynamicSection(image, 0); + return B_OK; } @@ -458,7 +487,7 @@ error1: template<typename Class> /*static*/ status_t -ELFLoader<Class>::_ParseDynamicSection(ImageType* image) +ELFLoader<Class>::_ParseDynamicSection(ImageType* image, AddrType delta) { image->syms = 0; image->rel = 0; @@ -469,10 +498,12 @@ ELFLoader<Class>::_ParseDynamicSection(ImageType* image) image->pltrel_len = 0; image->pltrel_type = 0; - DynType* d = (DynType*)image->dynamic_section.start; - if (!d) + if(image->dynamic_section.start == 0) return B_ERROR; + DynType* d = (DynType*)(addr_t)(image->dynamic_section.start + + image->mapped_delta); + for (int i = 0; d[i].d_tag != DT_NULL; i++) { switch (d[i].d_tag) { case DT_HASH: @@ -480,25 +511,25 @@ ELFLoader<Class>::_ParseDynamicSection(ImageType* image) break; case DT_SYMTAB: image->syms = (SymType*)(d[i].d_un.d_ptr - + image->text_region.delta); + + image->text_region.delta + delta); break; case DT_REL: image->rel = (RelType*)(d[i].d_un.d_ptr - + image->text_region.delta); + + image->text_region.delta + delta); break; case DT_RELSZ: image->rel_len = d[i].d_un.d_val; break; case DT_RELA: image->rela = (RelaType*)(d[i].d_un.d_ptr - + image->text_region.delta); + + image->text_region.delta + delta); break; case DT_RELASZ: image->rela_len = d[i].d_un.d_val; break; case DT_JMPREL: image->pltrel = (RelType*)(d[i].d_un.d_ptr - + image->text_region.delta); + + image->text_region.delta + delta); break; case DT_PLTRELSZ: image->pltrel_len = d[i].d_un.d_val; diff --git a/src/system/boot/platform/amiga_m68k/mmu.cpp b/src/system/boot/platform/amiga_m68k/mmu.cpp index 03f56b9..040c34a 100644 --- a/src/system/boot/platform/amiga_m68k/mmu.cpp +++ b/src/system/boot/platform/amiga_m68k/mmu.cpp @@ -662,6 +662,20 @@ platform_free_region(void *address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + void *address = mmu_allocate((void *)*_address, size); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + + void platform_release_heap(struct stage2_args *args, void *base) { diff --git a/src/system/boot/platform/atari_m68k/mmu.cpp b/src/system/boot/platform/atari_m68k/mmu.cpp index 7132119..77759cd 100644 --- a/src/system/boot/platform/atari_m68k/mmu.cpp +++ b/src/system/boot/platform/atari_m68k/mmu.cpp @@ -666,6 +666,20 @@ platform_free_region(void *address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + void *address = mmu_allocate((void *)*_address, size); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + + void platform_release_heap(struct stage2_args *args, void *base) { diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp b/src/system/boot/platform/bios_ia32/mmu.cpp index cfed3de..344abf7 100644 --- a/src/system/boot/platform/bios_ia32/mmu.cpp +++ b/src/system/boot/platform/bios_ia32/mmu.cpp @@ -769,6 +769,43 @@ platform_free_region(void *address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + void *address = mmu_allocate((void *)*_address, size); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + + +status_t +platform_allocate_elf_region(uint64 *_address, uint64 size, uint8 protection, + void **_mappedAddress) +{ + // The 64-bit kernel is loaded to 0xFFFFFFFF80000000. You'll notice that + // the low 32 bits of this address are the same as the 32-bit KERNEL_BASE + // (0x80000000). Therefore, the way this function is implemented is to use + // mmu_allocate() and then set the upper 32 bits to all 1s. The long mode + // switch code will remap everything to the correct addresses. + + void *address = mmu_allocate((void *)(addr_t)(*_address & 0xFFFFFFFF), size); + if (address == NULL) + return B_NO_MEMORY; + + // This is the address that the ELF loading code will access the mapping + // through. + *_mappedAddress = address; + + *_address = (uint64)(uint32)address | 0xFFFFFFFF00000000LL; + return B_OK; +} + + void platform_release_heap(struct stage2_args *args, void *base) { diff --git a/src/system/boot/platform/cfe/mmu.cpp b/src/system/boot/platform/cfe/mmu.cpp index b9f3143..e33ebe7 100644 --- a/src/system/boot/platform/cfe/mmu.cpp +++ b/src/system/boot/platform/cfe/mmu.cpp @@ -34,3 +34,21 @@ platform_free_region(void *address, size_t size) return arch_mmu_free(address, size); } + +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + if (size == 0) + return B_BAD_VALUE; + + void *address = arch_mmu_allocate((void *)*_address, size, protection, + exactAddress); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + diff --git a/src/system/boot/platform/openfirmware/mmu.cpp b/src/system/boot/platform/openfirmware/mmu.cpp index b765e8d..f284dae 100644 --- a/src/system/boot/platform/openfirmware/mmu.cpp +++ b/src/system/boot/platform/openfirmware/mmu.cpp @@ -34,3 +34,21 @@ platform_free_region(void *address, size_t size) return arch_mmu_free(address, size); } + +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + if (size == 0) + return B_BAD_VALUE; + + void *address = arch_mmu_allocate((void *)*_address, size, protection, + exactAddress); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + diff --git a/src/system/boot/platform/raspberrypi_arm/mmu.cpp b/src/system/boot/platform/raspberrypi_arm/mmu.cpp index 18c39f5..3b52275 100644 --- a/src/system/boot/platform/raspberrypi_arm/mmu.cpp +++ b/src/system/boot/platform/raspberrypi_arm/mmu.cpp @@ -678,6 +678,20 @@ platform_free_region(void *address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ + void *address = mmu_allocate((void *)*_address, size); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +} + + void platform_release_heap(struct stage2_args *args, void *base) { diff --git a/src/system/boot/platform/routerboard_mipsel/mmu.cpp b/src/system/boot/platform/routerboard_mipsel/mmu.cpp index cb46119..744cac4 100644 --- a/src/system/boot/platform/routerboard_mipsel/mmu.cpp +++ b/src/system/boot/platform/routerboard_mipsel/mmu.cpp @@ -79,6 +79,15 @@ platform_free_region(void* address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ +#warning IMPLEMENT platform_allocate_elf_region + return B_ERROR; +} + + void platform_release_heap(struct stage2_args* args, void* base) { diff --git a/src/system/boot/platform/u-boot/mmu.cpp b/src/system/boot/platform/u-boot/mmu.cpp index e38d931..a1d001c 100644 --- a/src/system/boot/platform/u-boot/mmu.cpp +++ b/src/system/boot/platform/u-boot/mmu.cpp @@ -681,6 +681,24 @@ platform_free_region(void *address, size_t size) } +status_t +platform_allocate_elf_region(uint32 *_address, uint32 size, uint8 protection, + void **_mappedAddress) +{ +#ifdef __ARM__ + void *address = mmu_allocate((void *)*_address, size); + if (address == NULL) + return B_NO_MEMORY; + + *_address = (uint32)address; + *_mappedAddress = address; + return B_OK; +#else + return B_ERROR; +#endif +} + + void platform_release_heap(struct stage2_args *args, void *base) { diff --git a/src/system/kernel/arch/x86_64/arch_elf.cpp b/src/system/kernel/arch/x86_64/arch_elf.cpp index b0cbe9d..09d2f34 100644 --- a/src/system/kernel/arch/x86_64/arch_elf.cpp +++ b/src/system/kernel/arch/x86_64/arch_elf.cpp @@ -136,31 +136,36 @@ boot_arch_elf_relocate_rela(preloaded_elf64_image* image, Elf64_Rela* rel, } // Address of the relocation. - Elf64_Addr* resolveAddr = (Elf64_Addr *)(image->text_region.delta - + rel[i].r_offset); + Elf64_Addr relocAddr = image->text_region.delta + rel[i].r_offset; - // Perform the relocation. + // Calculate the relocation value. + Elf64_Addr relocValue; switch(type) { case R_X86_64_NONE: - break; + continue; case R_X86_64_64: - *resolveAddr = symAddr + rel[i].r_addend; + relocValue = symAddr + rel[i].r_addend; break; case R_X86_64_PC32: - *resolveAddr = symAddr + rel[i].r_addend - rel[i].r_offset; + relocValue = symAddr + rel[i].r_addend - rel[i].r_offset; break; case R_X86_64_GLOB_DAT: case R_X86_64_JUMP_SLOT: - *resolveAddr = symAddr + rel[i].r_addend; + relocValue = symAddr + rel[i].r_addend; break; case R_X86_64_RELATIVE: - *resolveAddr = image->text_region.delta + rel[i].r_addend; + relocValue = image->text_region.delta + rel[i].r_addend; break; default: dprintf("arch_elf_relocate_rel: unhandled relocation type %d\n", type); return B_BAD_DATA; } +#ifdef _BOOT_MODE + boot_elf_set_relocation(image, relocAddr, relocValue); +#else + *(Elf64_Addr *)relocAddr = relocValue; +#endif } return B_OK;