root/src/system/kernel/arch/m68k/arch_elf.cpp
/*
 * Copyright 2007, François Revol, revol@free.fr.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
 * All rights reserved. Distributed under the terms of the MIT License.
 *
 *
 * Copyright 2002, Travis Geiselbrecht. All rights reserved.
 * Distributed under the terms of the NewOS License.
 */

#ifdef _BOOT_MODE
#include <boot/arch.h>
#endif

#include <KernelExport.h>

#include <elf_priv.h>
#include <arch/elf.h>


//#define TRACE_ARCH_ELF
#ifdef TRACE_ARCH_ELF
#       define TRACE(x) dprintf x
#       define CHATTY 1
#else
#       define TRACE(x) ;
#       define CHATTY 0
#endif


#ifdef TRACE_ARCH_ELF
static const char *kRelocations[] = {
        "R_68K_NONE",
        "R_68K_32",     /* Direct 32 bit  */
        "R_68K_16",     /* Direct 16 bit  */
        "R_68K_8",      /* Direct 8 bit  */
        "R_68K_PC32",   /* PC relative 32 bit */
        "R_68K_PC16",   /* PC relative 16 bit */
        "R_68K_PC8",    /* PC relative 8 bit */
        "R_68K_GOT32",  /* 32 bit PC relative GOT entry */
        "R_68K_GOT16",  /* 16 bit PC relative GOT entry */
        "R_68K_GOT8",   /* 8 bit PC relative GOT entry */
        "R_68K_GOT32O", /* 32 bit GOT offset */
        "R_68K_GOT16O", /* 16 bit GOT offset */
        "R_68K_GOT8O",  /* 8 bit GOT offset */
        "R_68K_PLT32",  /* 32 bit PC relative PLT address */
        "R_68K_PLT16",  /* 16 bit PC relative PLT address */
        "R_68K_PLT8",   /* 8 bit PC relative PLT address */
        "R_68K_PLT32O", /* 32 bit PLT offset */
        "R_68K_PLT16O", /* 16 bit PLT offset */
        "R_68K_PLT8O",  /* 8 bit PLT offset */
        "R_68K_COPY",   /* Copy symbol at runtime */
        "R_68K_GLOB_DAT",       /* Create GOT entry */
        "R_68K_JMP_SLOT",       /* Create PLT entry */
        "R_68K_RELATIVE",       /* Adjust by program base */
        /* These are GNU extensions to enable C++ vtable garbage collection.  */
        "R_68K_GNU_VTINHERIT",
        "R_68K_GNU_VTENTRY",
#if 0
        "R_386_NONE",
        "R_386_32",                     /* add symbol value */
        "R_386_PC32",           /* add PC relative symbol value */
        "R_386_GOT32",          /* add PC relative GOT offset */
        "R_386_PLT32",          /* add PC relative PLT offset */
        "R_386_COPY",           /* copy data from shared object */
        "R_386_GLOB_DAT",       /* set GOT entry to data address */
        "R_386_JMP_SLOT",       /* set GOT entry to code address */
        "R_386_RELATIVE",       /* add load address of shared object */
        "R_386_GOTOFF",         /* add GOT relative symbol address */
        "R_386_GOTPC",          /* add PC relative GOT table address */
#endif
};
#endif

#ifdef _BOOT_MODE
status_t
boot_arch_elf_relocate_rel(struct preloaded_elf32_image *image, Elf32_Rel *rel,
        int rel_len)
#else
int
arch_elf_relocate_rel(struct elf_image_info *image,
        struct elf_image_info *resolve_image, Elf32_Rel *rel, int rel_len)
#endif
{
        // there are no rel entries in M68K elf
        return B_NO_ERROR;
}


static inline void
write_32(addr_t P, Elf32_Word value)
{
        *(Elf32_Word*)P = value;
}


static inline void
write_16(addr_t P, Elf32_Word value)
{
        // bits 16:29
        *(Elf32_Half*)P = (Elf32_Half)value;
}


static inline bool
write_16_check(addr_t P, Elf32_Word value)
{
        // bits 15:0
        if ((value & 0xffff0000) && (~value & 0xffff8000))
                return false;
        *(Elf32_Half*)P = (Elf32_Half)value;
        return true;
}


static inline bool
write_8(addr_t P, Elf32_Word value)
{
        // bits 7:0
        *(uint8 *)P = (uint8)value;
        return true;
}


static inline bool
write_8_check(addr_t P, Elf32_Word value)
{
        // bits 7:0
        if ((value & 0xffffff00) && (~value & 0xffffff80))
                return false;
        *(uint8 *)P = (uint8)value;
        return true;
}


#ifdef _BOOT_MODE
status_t
boot_arch_elf_relocate_rela(struct preloaded_elf32_image *image,
        Elf32_Rela *rel, int rel_len)
#else
int
arch_elf_relocate_rela(struct elf_image_info *image,
        struct elf_image_info *resolve_image, Elf32_Rela *rel, int rel_len)
#endif
{
        int i;
        Elf32_Sym *sym;
        int vlErr;
        elf_addr S = 0; // symbol address
        addr_t R = 0;   // section relative symbol address

        addr_t G = 0;   // GOT address
        addr_t L = 0;   // PLT address

        #define P       ((addr_t)(image->text_region.delta + rel[i].r_offset))
        #define A       ((addr_t)rel[i].r_addend)
        #define B       (image->text_region.delta)

        // TODO: Get the GOT address!
        #define REQUIRE_GOT     \
                if (G == 0) {   \
                        dprintf("arch_elf_relocate_rela(): Failed to get GOT address!\n"); \
                        return B_ERROR; \
                }

        // TODO: Get the PLT address!
        #define REQUIRE_PLT     \
                if (L == 0) {   \
                        dprintf("arch_elf_relocate_rela(): Failed to get PLT address!\n"); \
                        return B_ERROR; \
                }

        for (i = 0; i * (int)sizeof(Elf32_Rela) < rel_len; i++) {
#if CHATTY
                dprintf("looking at rel type %d, offset 0x%lx, sym 0x%lx, addend 0x%lx\n",
                        ELF32_R_TYPE(rel[i].r_info), rel[i].r_offset, ELF32_R_SYM(rel[i].r_info), rel[i].r_addend);
#endif
                switch (ELF32_R_TYPE(rel[i].r_info)) {
                        case R_68K_32:
                        case R_68K_16:
                        case R_68K_8:
                        case R_68K_PC32:
                        case R_68K_PC16:
                        case R_68K_PC8:
                        case R_68K_GLOB_DAT:
                        case R_68K_JMP_SLOT:
                                sym = SYMBOL(image, ELF32_R_SYM(rel[i].r_info));

#ifdef _BOOT_MODE
                                vlErr = boot_elf_resolve_symbol(image, sym, &S);
#else
                                vlErr = elf_resolve_symbol(image, sym, resolve_image, &S);
#endif
                                if (vlErr < 0) {
                                        dprintf("%s(): Failed to relocate "
                                                "entry index %d, rel type %d, offset 0x%lx, sym 0x%lx, "
                                                "addend 0x%lx\n", __FUNCTION__, i, ELF32_R_TYPE(rel[i].r_info),
                                                rel[i].r_offset, ELF32_R_SYM(rel[i].r_info),
                                                rel[i].r_addend);
                                        return vlErr;
                                }
                                break;
                }

                switch (ELF32_R_TYPE(rel[i].r_info)) {
                        case R_68K_NONE:
                                break;

                        case R_68K_COPY:
                                // TODO: Implement!
                                dprintf("arch_elf_relocate_rela(): R_68K_COPY not yet "
                                        "supported!\n");
                                return B_ERROR;

                        case R_68K_32:
                        case R_68K_GLOB_DAT:
                                write_32(P, S + A);
                                break;

                        case R_68K_16:
                                if (write_16_check(P, S + A))
                                        break;
dprintf("R_68K_16 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_8:
                                if (write_8_check(P, S + A))
                                        break;
dprintf("R_68K_8 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_PC32:
                                write_32(P, (S + A - P));
                                break;

                        case R_68K_PC16:
                                if (write_16_check(P, (S + A - P)))
                                        break;
dprintf("R_68K_PC16 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_PC8:
                                if (write_8(P, (S + A - P)))
                                        break;
dprintf("R_68K_PC8 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_GOT32:
                                REQUIRE_GOT;
                                write_32(P, (G + A - P));
                                break;

                        case R_68K_GOT16:
                                REQUIRE_GOT;
                                if (write_16_check(P, (G + A - P)))
                                        break;
dprintf("R_68K_GOT16 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_GOT8:
                                REQUIRE_GOT;
                                if (write_8_check(P, (G + A - P)))
                                        break;
dprintf("R_68K_GOT8 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_GOT32O:
                                REQUIRE_GOT;
                                write_32(P, (G + A));
                                break;

                        case R_68K_GOT16O:
                                REQUIRE_GOT;
                                if (write_16_check(P, (G + A)))
                                        break;
dprintf("R_68K_GOT16 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_GOT8O:
                                REQUIRE_GOT;
                                if (write_8_check(P, (G + A)))
                                        break;
dprintf("R_68K_GOT8 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_JMP_SLOT:
                                write_32(P, S + A);
                                break;

                        case R_68K_RELATIVE:
                                write_32(P, B + A);
                                break;

                        case R_68K_PLT32:
                                REQUIRE_PLT;
                                write_32(P, (L + A - P));
                                break;

                        case R_68K_PLT16:
                                REQUIRE_PLT;
                                if (write_16_check(P, (L + A - P)))
                                        break;
dprintf("R_68K_PLT16 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_PLT8:
                                REQUIRE_PLT;
                                if (write_8_check(P, (L + A - P)))
                                        break;
dprintf("R_68K_PLT8 overflow\n");
                                return B_BAD_DATA;

                        case R_68K_PLT32O:
                                REQUIRE_PLT;
                                write_32(P, (L + A));
                                break;

                        case R_68K_PLT16O:
                                REQUIRE_PLT;
                                if (write_16_check(P, (L + A)))
                                        break;
dprintf("R_68K_PLT16O overflow\n");
                                return B_BAD_DATA;

                        case R_68K_PLT8O:
                                REQUIRE_PLT;
                                if (write_8_check(P, (L + A)))
                                        break;
dprintf("R_68K_PLT8O overflow\n");
                                return B_BAD_DATA;

                        default:
                                dprintf("arch_elf_relocate_rela: unhandled relocation type %d\n", ELF32_R_TYPE(rel[i].r_info));
                                return B_ERROR;
                }
        }

        return B_NO_ERROR;
}