#include <elf.h>
#include <OS.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <AutoDeleter.h>
#include <BytePointer.h>
#include <commpage.h>
#include <driver_settings.h>
#include <boot/kernel_args.h>
#include <debug.h>
#include <image_defs.h>
#include <kernel.h>
#include <kimage.h>
#include <syscalls.h>
#include <team.h>
#include <thread.h>
#include <runtime_loader.h>
#include <util/AutoLock.h>
#include <StackOrHeapArray.h>
#include <vfs.h>
#include <vm/vm.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include <vm/VMArea.h>
#include <arch/cpu.h>
#include <arch/elf.h>
#include <elf_priv.h>
#include <boot/elf.h>
#ifdef TRACE_ELF
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
namespace {
#define IMAGE_HASH_SIZE 16
struct ImageHashDefinition {
typedef struct elf_image_info ValueType;
typedef image_id KeyType;
size_t Hash(ValueType* entry) const
{ return HashKey(entry->id); }
ValueType*& GetLink(ValueType* entry) const
{ return entry->next; }
size_t HashKey(KeyType key) const
{
return (size_t)key;
}
bool Compare(KeyType key, ValueType* entry) const
{
return key == entry->id;
}
};
typedef BOpenHashTable<ImageHashDefinition> ImageHash;
}
#ifndef ELF32_COMPAT
static ImageHash *sImagesHash;
static struct elf_image_info *sKernelImage = NULL;
static mutex sImageMutex = MUTEX_INITIALIZER("kimages_lock");
static mutex sImageLoadMutex = MUTEX_INITIALIZER("kimages_load_lock");
static bool sLoadElfSymbols = false;
static bool sInitialized = false;
static elf_sym *elf_find_symbol(struct elf_image_info *image, const char *name,
const elf_version_info *version, bool lookupDefault);
static void
unregister_elf_image(struct elf_image_info *image)
{
unregister_image(team_get_kernel_team(), image->id);
sImagesHash->Remove(image);
}
static void
register_elf_image(struct elf_image_info *image)
{
extended_image_info imageInfo;
memset(&imageInfo, 0, sizeof(imageInfo));
imageInfo.basic_info.id = image->id;
imageInfo.basic_info.type = B_SYSTEM_IMAGE;
strlcpy(imageInfo.basic_info.name, image->name,
sizeof(imageInfo.basic_info.name));
imageInfo.basic_info.text = (void *)image->text_region.start;
imageInfo.basic_info.text_size = image->text_region.size;
imageInfo.basic_info.data = (void *)image->data_region.start;
imageInfo.basic_info.data_size = image->data_region.size;
if (image->text_region.id >= 0) {
imageInfo.basic_info.api_version = 0;
elf_sym* symbol = elf_find_symbol(image,
B_SHARED_OBJECT_HAIKU_VERSION_VARIABLE_NAME, NULL, true);
if (symbol != NULL && symbol->st_shndx != SHN_UNDEF
&& symbol->st_value > 0
&& symbol->Type() == STT_OBJECT
&& symbol->st_size >= sizeof(uint32)) {
addr_t symbolAddress = symbol->st_value + image->text_region.delta;
if (symbolAddress >= image->text_region.start
&& symbolAddress - image->text_region.start + sizeof(uint32)
<= image->text_region.size) {
imageInfo.basic_info.api_version = *(uint32*)symbolAddress;
}
}
imageInfo.basic_info.abi = 0;
symbol = elf_find_symbol(image,
B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME, NULL, true);
if (symbol != NULL && symbol->st_shndx != SHN_UNDEF
&& symbol->st_value > 0
&& symbol->Type() == STT_OBJECT
&& symbol->st_size >= sizeof(uint32)) {
addr_t symbolAddress = symbol->st_value + image->text_region.delta;
if (symbolAddress >= image->text_region.start
&& symbolAddress - image->text_region.start + sizeof(uint32)
<= image->text_region.size) {
imageInfo.basic_info.api_version = *(uint32*)symbolAddress;
}
}
} else {
imageInfo.basic_info.api_version = B_HAIKU_VERSION;
imageInfo.basic_info.abi = B_HAIKU_ABI;
}
image->id = register_image(team_get_kernel_team(), &imageInfo,
sizeof(imageInfo));
sImagesHash->Insert(image);
}
static struct elf_image_info *
find_image_at_address(addr_t address)
{
#if KDEBUG
if (!debug_debugger_running())
ASSERT_LOCKED_MUTEX(&sImageMutex);
#endif
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
struct elf_image_info* image = iterator.Next();
if ((address >= image->text_region.start && address
<= (image->text_region.start + image->text_region.size))
|| (address >= image->data_region.start
&& address
<= (image->data_region.start + image->data_region.size)))
return image;
}
return NULL;
}
static int
dump_address_info(int argc, char **argv)
{
const char *symbol, *imageName;
bool exactMatch;
addr_t address, baseAddress;
if (argc < 2) {
kprintf("usage: ls <address>\n");
return 0;
}
address = strtoul(argv[1], NULL, 16);
status_t error;
if (IS_KERNEL_ADDRESS(address)) {
error = elf_debug_lookup_symbol_address(address, &baseAddress, &symbol,
&imageName, &exactMatch);
} else {
error = elf_debug_lookup_user_symbol_address(
debug_get_debugged_thread()->team, address, &baseAddress, &symbol,
&imageName, &exactMatch);
}
if (error == B_OK) {
kprintf("%p = %s + 0x%lx (%s)%s\n", (void*)address, symbol,
address - baseAddress, imageName, exactMatch ? "" : " (nearest)");
} else
kprintf("There is no image loaded at this address!\n");
return 0;
}
static struct elf_image_info *
find_image(image_id id)
{
return sImagesHash->Lookup(id);
}
static struct elf_image_info *
find_image_by_vnode(void *vnode)
{
MutexLocker locker(sImageMutex);
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
struct elf_image_info* image = iterator.Next();
if (image->vnode == vnode)
return image;
}
return NULL;
}
#endif
static struct elf_image_info *
create_image_struct()
{
struct elf_image_info *image
= (struct elf_image_info *)malloc(sizeof(struct elf_image_info));
if (image == NULL)
return NULL;
memset(image, 0, sizeof(struct elf_image_info));
image->text_region.id = -1;
image->data_region.id = -1;
image->ref_count = 1;
return image;
}
static void
delete_elf_image(struct elf_image_info *image)
{
if (image->text_region.id >= 0)
delete_area(image->text_region.id);
if (image->data_region.id >= 0)
delete_area(image->data_region.id);
if (image->vnode)
vfs_put_vnode(image->vnode);
free(image->versions);
free(image->debug_symbols);
free((void*)image->debug_string_table);
free(image->elf_header);
free(image->name);
free(image);
}
static const char *
get_symbol_type_string(elf_sym *symbol)
{
switch (symbol->Type()) {
case STT_FUNC:
return "func";
case STT_OBJECT:
return " obj";
case STT_FILE:
return "file";
default:
return "----";
}
}
static const char *
get_symbol_bind_string(elf_sym *symbol)
{
switch (symbol->Bind()) {
case STB_LOCAL:
return "loc ";
case STB_GLOBAL:
return "glob";
case STB_WEAK:
return "weak";
default:
return "----";
}
}
#ifndef ELF32_COMPAT
static int
dump_symbol(int argc, char **argv)
{
if (argc != 2 || !strcmp(argv[1], "--help")) {
kprintf("usage: %s <symbol-name>\n", argv[0]);
return 0;
}
struct elf_image_info *image = NULL;
const char *pattern = argv[1];
void* symbolAddress = NULL;
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
image = iterator.Next();
if (image->num_debug_symbols > 0) {
for (uint32 i = 0; i < image->num_debug_symbols; i++) {
elf_sym *symbol = &image->debug_symbols[i];
const char *name = image->debug_string_table + symbol->st_name;
if (symbol->st_value > 0 && strstr(name, pattern) != 0) {
symbolAddress
= (void*)(symbol->st_value + image->text_region.delta);
kprintf("%p %5lu %s:%s\n", symbolAddress,
(long unsigned int)(symbol->st_size),
image->name, name);
}
}
} else {
for (uint32 i = 0; i < HASHTABSIZE(image); i++) {
for (uint32 j = HASHBUCKETS(image)[i]; j != STN_UNDEF;
j = HASHCHAINS(image)[j]) {
elf_sym *symbol = &image->syms[j];
const char *name = SYMNAME(image, symbol);
if (symbol->st_value > 0 && strstr(name, pattern) != 0) {
symbolAddress = (void*)(symbol->st_value
+ image->text_region.delta);
kprintf("%p %5lu %s:%s\n", symbolAddress,
(long unsigned int)(symbol->st_size),
image->name, name);
}
}
}
}
}
if (symbolAddress != NULL)
set_debug_variable("_", (addr_t)symbolAddress);
return 0;
}
static int
dump_symbols(int argc, char **argv)
{
struct elf_image_info *image = NULL;
uint32 i;
if (argc > 1) {
if (isdigit(argv[1][0])) {
addr_t num = strtoul(argv[1], NULL, 0);
if (IS_KERNEL_ADDRESS(num)) {
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
elf_image_info* current = iterator.Next();
if (current->text_region.start <= num
&& current->text_region.start
+ current->text_region.size >= num) {
image = current;
break;
}
}
if (image == NULL) {
kprintf("No image covers %#" B_PRIxADDR " in the kernel!\n",
num);
}
} else {
image = sImagesHash->Lookup(num);
if (image == NULL) {
kprintf("image %#" B_PRIxADDR " doesn't exist in the "
"kernel!\n", num);
}
}
} else {
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
elf_image_info* current = iterator.Next();
if (!strcmp(current->name, argv[1])) {
image = current;
break;
}
}
if (image == NULL)
kprintf("No image \"%s\" found in kernel!\n", argv[1]);
}
} else {
kprintf("usage: %s image_name/image_id/address_in_image\n", argv[0]);
return 0;
}
if (image == NULL)
return -1;
kprintf("Symbols of image %" B_PRId32 " \"%s\":\n", image->id, image->name);
kprintf("%-*s Type Size Name\n", B_PRINTF_POINTER_WIDTH, "Address");
if (image->num_debug_symbols > 0) {
for (i = 0; i < image->num_debug_symbols; i++) {
elf_sym *symbol = &image->debug_symbols[i];
if (symbol->st_value == 0 || symbol->st_size
>= image->text_region.size + image->data_region.size)
continue;
kprintf("%0*lx %s/%s %5ld %s\n", B_PRINTF_POINTER_WIDTH,
symbol->st_value + image->text_region.delta,
get_symbol_type_string(symbol), get_symbol_bind_string(symbol),
(long unsigned int)(symbol->st_size),
image->debug_string_table + symbol->st_name);
}
} else {
int32 j;
for (i = 0; i < HASHTABSIZE(image); i++) {
for (j = HASHBUCKETS(image)[i]; j != STN_UNDEF;
j = HASHCHAINS(image)[j]) {
elf_sym *symbol = &image->syms[j];
if (symbol->st_value == 0 || symbol->st_size
>= image->text_region.size + image->data_region.size)
continue;
kprintf("%08lx %s/%s %5ld %s\n",
symbol->st_value + image->text_region.delta,
get_symbol_type_string(symbol),
get_symbol_bind_string(symbol),
(long unsigned int)(symbol->st_size),
SYMNAME(image, symbol));
}
}
}
return 0;
}
static void
dump_elf_region(struct elf_region *region, const char *name)
{
kprintf(" %s.id %" B_PRId32 "\n", name, region->id);
kprintf(" %s.start %#" B_PRIxADDR "\n", name, region->start);
kprintf(" %s.size %#" B_PRIxSIZE "\n", name, region->size);
kprintf(" %s.delta %ld\n", name, region->delta);
}
static void
dump_image_info(struct elf_image_info *image)
{
kprintf("elf_image_info at %p:\n", image);
kprintf(" next %p\n", image->next);
kprintf(" id %" B_PRId32 "\n", image->id);
dump_elf_region(&image->text_region, "text");
dump_elf_region(&image->data_region, "data");
kprintf(" dynamic_section %#" B_PRIxADDR "\n", image->dynamic_section);
kprintf(" needed %p\n", image->needed);
kprintf(" symhash %p\n", image->symhash);
kprintf(" syms %p\n", image->syms);
kprintf(" strtab %p\n", image->strtab);
kprintf(" rel %p\n", image->rel);
kprintf(" rel_len %#x\n", image->rel_len);
kprintf(" rela %p\n", image->rela);
kprintf(" rela_len %#x\n", image->rela_len);
kprintf(" pltrel %p\n", image->pltrel);
kprintf(" pltrel_len %#x\n", image->pltrel_len);
kprintf(" debug_symbols %p (%" B_PRIu32 ")\n",
image->debug_symbols, image->num_debug_symbols);
}
static int
dump_image(int argc, char **argv)
{
struct elf_image_info *image;
if (argc > 1) {
addr_t num = strtoul(argv[1], NULL, 0);
if (IS_KERNEL_ADDRESS(num)) {
dump_image_info((struct elf_image_info *)num);
} else {
image = sImagesHash->Lookup(num);
if (image == NULL) {
kprintf("image %#" B_PRIxADDR " doesn't exist in the kernel!\n",
num);
} else
dump_image_info(image);
}
return 0;
}
kprintf("loaded kernel images:\n");
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
image = iterator.Next();
kprintf("%p (%" B_PRId32 ") %s\n", image, image->id, image->name);
}
return 0;
}
#endif
static uint32
elf_hash(const char* _name)
{
const uint8* name = (const uint8*)_name;
uint32 h = 0;
while (*name != '\0') {
h = (h << 4) + *name++;
h ^= (h >> 24) & 0xf0;
}
return (h & 0x0fffffff);
}
static elf_sym *
elf_find_symbol(struct elf_image_info *image, const char *name,
const elf_version_info *lookupVersion, bool lookupDefault)
{
if (image->dynamic_section == 0 || HASHTABSIZE(image) == 0)
return NULL;
elf_sym* versionedSymbol = NULL;
uint32 versionedSymbolCount = 0;
uint32 hash = elf_hash(name) % HASHTABSIZE(image);
for (uint32 i = HASHBUCKETS(image)[hash]; i != STN_UNDEF;
i = HASHCHAINS(image)[i]) {
elf_sym* symbol = &image->syms[i];
if (symbol->st_shndx == SHN_UNDEF
|| ((symbol->Bind() != STB_GLOBAL) && (symbol->Bind() != STB_WEAK))
|| strcmp(SYMNAME(image, symbol), name) != 0) {
continue;
}
if (image->symbol_versions == NULL) {
if (lookupVersion == NULL) {
return symbol;
}
dprintf("Kernel add-on requires version support, but the kernel "
"is too old.\n");
return NULL;
}
uint32 versionID = image->symbol_versions[i];
uint32 versionIndex = VER_NDX(versionID);
elf_version_info& version = image->versions[versionIndex];
if (versionIndex == VER_NDX_LOCAL)
continue;
if (lookupVersion != NULL) {
if (version.hash == lookupVersion->hash
&& strcmp(version.name, lookupVersion->name) == 0) {
return symbol;
}
if ((versionID & VER_NDX_FLAG_HIDDEN) == 0
&& versionIndex == VER_NDX_GLOBAL
&& !lookupDefault) {
return symbol;
}
} else {
if (versionIndex == VER_NDX_GLOBAL
|| (!lookupDefault && versionIndex == VER_NDX_INITIAL)) {
return symbol;
}
if ((versionID & VER_NDX_FLAG_HIDDEN) == 0) {
versionedSymbolCount++;
versionedSymbol = symbol;
}
}
}
return versionedSymbolCount == 1 ? versionedSymbol : NULL;
}
static status_t
elf_parse_dynamic_section(struct elf_image_info *image)
{
elf_dyn *d;
ssize_t neededOffset = -1;
TRACE(("top of elf_parse_dynamic_section\n"));
image->symhash = 0;
image->syms = 0;
image->strtab = 0;
d = (elf_dyn *)image->dynamic_section;
if (!d)
return B_ERROR;
for (int32 i = 0; d[i].d_tag != DT_NULL; i++) {
switch (d[i].d_tag) {
case DT_NEEDED:
neededOffset = d[i].d_un.d_ptr + image->text_region.delta;
break;
case DT_HASH:
image->symhash = (uint32 *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_STRTAB:
image->strtab = (char *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_SYMTAB:
image->syms = (elf_sym *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_REL:
image->rel = (elf_rel *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_RELSZ:
image->rel_len = d[i].d_un.d_val;
break;
case DT_RELA:
image->rela = (elf_rela *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_RELASZ:
image->rela_len = d[i].d_un.d_val;
break;
case DT_JMPREL:
image->pltrel = (elf_rel *)(d[i].d_un.d_ptr
+ image->text_region.delta);
break;
case DT_PLTRELSZ:
image->pltrel_len = d[i].d_un.d_val;
break;
case DT_PLTREL:
image->pltrel_type = d[i].d_un.d_val;
break;
case DT_VERSYM:
image->symbol_versions = (elf_versym*)
(d[i].d_un.d_ptr + image->text_region.delta);
break;
case DT_VERDEF:
image->version_definitions = (elf_verdef*)
(d[i].d_un.d_ptr + image->text_region.delta);
break;
case DT_VERDEFNUM:
image->num_version_definitions = d[i].d_un.d_val;
break;
case DT_VERNEED:
image->needed_versions = (elf_verneed*)
(d[i].d_un.d_ptr + image->text_region.delta);
break;
case DT_VERNEEDNUM:
image->num_needed_versions = d[i].d_un.d_val;
break;
case DT_SYMBOLIC:
image->symbolic = true;
break;
case DT_FLAGS:
{
uint32 flags = d[i].d_un.d_val;
if ((flags & DF_SYMBOLIC) != 0)
image->symbolic = true;
break;
}
default:
continue;
}
}
if (!image->symhash || !image->syms || !image->strtab)
return B_ERROR;
TRACE(("needed_offset = %ld\n", neededOffset));
if (neededOffset >= 0)
image->needed = STRING(image, neededOffset);
return B_OK;
}
#ifndef ELF32_COMPAT
static status_t
assert_defined_image_version(elf_image_info* dependentImage,
elf_image_info* image, const elf_version_info& neededVersion, bool weak)
{
if (image->version_definitions == NULL) {
dprintf("%s: No version information available (required by %s)\n",
image->name, dependentImage->name);
return B_OK;
}
BytePointer<elf_verdef> definition(image->version_definitions);
for (uint32 i = 0; i < image->num_version_definitions; i++) {
uint32 versionIndex = VER_NDX(definition->vd_ndx);
elf_version_info& info = image->versions[versionIndex];
if (neededVersion.hash == info.hash
&& strcmp(neededVersion.name, info.name) == 0) {
return B_OK;
}
definition += definition->vd_next;
}
if (!weak) {
dprintf("%s: version \"%s\" not found (required by %s)\n", image->name,
neededVersion.name, dependentImage->name);
return B_MISSING_SYMBOL;
}
return B_OK;
}
static status_t
init_image_version_infos(elf_image_info* image)
{
uint32 maxIndex = 0;
if (image->version_definitions != NULL) {
BytePointer<elf_verdef> definition(image->version_definitions);
for (uint32 i = 0; i < image->num_version_definitions; i++) {
if (definition->vd_version != 1) {
dprintf("Unsupported version definition revision: %u\n",
definition->vd_version);
return B_BAD_VALUE;
}
uint32 versionIndex = VER_NDX(definition->vd_ndx);
if (versionIndex > maxIndex)
maxIndex = versionIndex;
definition += definition->vd_next;
}
}
if (image->needed_versions != NULL) {
BytePointer<elf_verneed> needed(image->needed_versions);
for (uint32 i = 0; i < image->num_needed_versions; i++) {
if (needed->vn_version != 1) {
dprintf("Unsupported version needed revision: %u\n",
needed->vn_version);
return B_BAD_VALUE;
}
BytePointer<elf_vernaux> vernaux(needed + needed->vn_aux);
for (uint32 k = 0; k < needed->vn_cnt; k++) {
uint32 versionIndex = VER_NDX(vernaux->vna_other);
if (versionIndex > maxIndex)
maxIndex = versionIndex;
vernaux += vernaux->vna_next;
}
needed += needed->vn_next;
}
}
if (maxIndex == 0)
return B_OK;
image->versions
= (elf_version_info*)malloc(sizeof(elf_version_info) * (maxIndex + 1));
if (image->versions == NULL) {
dprintf("Memory shortage in init_image_version_infos()\n");
return B_NO_MEMORY;
}
image->num_versions = maxIndex + 1;
if (image->version_definitions != NULL) {
BytePointer<elf_verdef> definition(image->version_definitions);
for (uint32 i = 0; i < image->num_version_definitions; i++) {
if (definition->vd_cnt > 0
&& (definition->vd_flags & VER_FLG_BASE) == 0) {
BytePointer<elf_verdaux> verdaux(definition
+ definition->vd_aux);
uint32 versionIndex = VER_NDX(definition->vd_ndx);
elf_version_info& info = image->versions[versionIndex];
info.hash = definition->vd_hash;
info.name = STRING(image, verdaux->vda_name);
info.file_name = NULL;
}
definition += definition->vd_next;
}
}
if (image->needed_versions != NULL) {
BytePointer<elf_verneed> needed(image->needed_versions);
for (uint32 i = 0; i < image->num_needed_versions; i++) {
const char* fileName = STRING(image, needed->vn_file);
BytePointer<elf_vernaux> vernaux(needed + needed->vn_aux);
for (uint32 k = 0; k < needed->vn_cnt; k++) {
uint32 versionIndex = VER_NDX(vernaux->vna_other);
elf_version_info& info = image->versions[versionIndex];
info.hash = vernaux->vna_hash;
info.name = STRING(image, vernaux->vna_name);
info.file_name = fileName;
vernaux += vernaux->vna_next;
}
needed += needed->vn_next;
}
}
return B_OK;
}
static status_t
check_needed_image_versions(elf_image_info* image)
{
if (image->needed_versions == NULL)
return B_OK;
BytePointer<elf_verneed> needed(image->needed_versions);
for (uint32 i = 0; i < image->num_needed_versions; i++) {
elf_image_info* dependency = sKernelImage;
BytePointer<elf_vernaux> vernaux(needed + needed->vn_aux);
for (uint32 k = 0; k < needed->vn_cnt; k++) {
uint32 versionIndex = VER_NDX(vernaux->vna_other);
elf_version_info& info = image->versions[versionIndex];
status_t error = assert_defined_image_version(image, dependency,
info, (vernaux->vna_flags & VER_FLG_WEAK) != 0);
if (error != B_OK)
return error;
vernaux += vernaux->vna_next;
}
needed += needed->vn_next;
}
return B_OK;
}
#endif
status_t
elf_resolve_symbol(struct elf_image_info *image, elf_sym *symbol,
struct elf_image_info *sharedImage, elf_addr *_symbolAddress)
{
if (symbol->Bind() == STB_LOCAL) {
*_symbolAddress = symbol->st_value + image->text_region.delta;
return B_OK;
}
elf_image_info* firstImage = sharedImage;
elf_image_info* secondImage = image;
if (image->symbolic)
std::swap(firstImage, secondImage);
const char *symbolName = SYMNAME(image, symbol);
const elf_version_info* versionInfo = NULL;
if (image->symbol_versions != NULL) {
uint32 index = symbol - image->syms;
uint32 versionIndex = VER_NDX(image->symbol_versions[index]);
if (versionIndex >= VER_NDX_INITIAL)
versionInfo = image->versions + versionIndex;
}
elf_image_info* foundImage = firstImage;
elf_sym* foundSymbol = elf_find_symbol(firstImage, symbolName, versionInfo,
false);
if (foundSymbol == NULL
|| foundSymbol->Bind() == STB_WEAK) {
elf_sym* secondSymbol = elf_find_symbol(secondImage, symbolName,
versionInfo, false);
if (secondSymbol != NULL
&& (foundSymbol == NULL
|| secondSymbol->Bind() != STB_WEAK)) {
foundImage = secondImage;
foundSymbol = secondSymbol;
}
}
if (foundSymbol == NULL) {
if (symbol->Bind() == STB_WEAK) {
*_symbolAddress = 0;
return B_OK;
}
dprintf("\"%s\": could not resolve symbol '%s'\n", image->name,
symbolName);
return B_MISSING_SYMBOL;
}
if (symbol->Type() != foundSymbol->Type()) {
dprintf("elf_resolve_symbol: found symbol '%s' in image '%s' "
"(requested by image '%s') but wrong type (%d vs. %d)\n",
symbolName, foundImage->name, image->name,
foundSymbol->Type(), symbol->Type());
return B_MISSING_SYMBOL;
}
*_symbolAddress = foundSymbol->st_value + foundImage->text_region.delta;
return B_OK;
}
static int
elf_relocate(struct elf_image_info* image, struct elf_image_info* resolveImage)
{
int status = B_NO_ERROR;
TRACE(("elf_relocate(%p (\"%s\"))\n", image, image->name));
if (image->rel) {
TRACE(("total %i rel relocs\n", image->rel_len / (int)sizeof(elf_rel)));
status = arch_elf_relocate_rel(image, resolveImage, image->rel,
image->rel_len);
if (status < B_OK)
return status;
}
if (image->pltrel) {
if (image->pltrel_type == DT_REL) {
TRACE(("total %i plt-relocs\n",
image->pltrel_len / (int)sizeof(elf_rel)));
status = arch_elf_relocate_rel(image, resolveImage, image->pltrel,
image->pltrel_len);
} else {
TRACE(("total %i plt-relocs\n",
image->pltrel_len / (int)sizeof(elf_rela)));
status = arch_elf_relocate_rela(image, resolveImage,
(elf_rela *)image->pltrel, image->pltrel_len);
}
if (status < B_OK)
return status;
}
if (image->rela) {
TRACE(("total %i rel relocs\n",
image->rela_len / (int)sizeof(elf_rela)));
status = arch_elf_relocate_rela(image, resolveImage, image->rela,
image->rela_len);
if (status < B_OK)
return status;
}
return status;
}
static int
verify_eheader(elf_ehdr *elfHeader)
{
if (memcmp(elfHeader->e_ident, ELFMAG, 4) != 0)
return B_NOT_AN_EXECUTABLE;
if (elfHeader->e_ident[4] != ELF_CLASS)
return B_NOT_AN_EXECUTABLE;
if (elfHeader->e_phoff == 0)
return B_NOT_AN_EXECUTABLE;
if (elfHeader->e_phentsize < sizeof(elf_phdr))
return B_NOT_AN_EXECUTABLE;
return 0;
}
#ifndef ELF32_COMPAT
static void
unload_elf_image(struct elf_image_info *image)
{
if (atomic_add(&image->ref_count, -1) > 1)
return;
TRACE(("unload image %" B_PRId32 ", %s\n", image->id, image->name));
unregister_elf_image(image);
delete_elf_image(image);
}
static status_t
load_elf_symbol_table(int fd, struct elf_image_info *image)
{
elf_ehdr *elfHeader = image->elf_header;
elf_sym *symbolTable = NULL;
elf_shdr *stringHeader = NULL;
uint32 numSymbols = 0;
char *stringTable;
status_t status;
ssize_t length;
int32 i;
ssize_t size = elfHeader->e_shnum * elfHeader->e_shentsize;
elf_shdr *sectionHeaders = (elf_shdr *)malloc(size);
if (sectionHeaders == NULL) {
dprintf("error allocating space for section headers\n");
return B_NO_MEMORY;
}
length = read_pos(fd, elfHeader->e_shoff, sectionHeaders, size);
if (length < size) {
TRACE(("error reading in program headers\n"));
status = B_ERROR;
goto error1;
}
for (i = 0; i < elfHeader->e_shnum; i++) {
if (sectionHeaders[i].sh_type == SHT_SYMTAB) {
stringHeader = §ionHeaders[sectionHeaders[i].sh_link];
if (stringHeader->sh_type != SHT_STRTAB) {
TRACE(("doesn't link to string table\n"));
status = B_BAD_DATA;
goto error1;
}
size = sectionHeaders[i].sh_size;
symbolTable = (elf_sym *)malloc(size);
if (symbolTable == NULL) {
status = B_NO_MEMORY;
goto error1;
}
length
= read_pos(fd, sectionHeaders[i].sh_offset, symbolTable, size);
if (length < size) {
TRACE(("error reading in symbol table\n"));
status = B_ERROR;
goto error2;
}
numSymbols = size / sizeof(elf_sym);
break;
}
}
if (symbolTable == NULL) {
TRACE(("no symbol table\n"));
status = B_BAD_VALUE;
goto error1;
}
stringTable = (char *)malloc(size = stringHeader->sh_size);
if (stringTable == NULL) {
status = B_NO_MEMORY;
goto error2;
}
length = read_pos(fd, stringHeader->sh_offset, stringTable, size);
if (length < size) {
TRACE(("error reading in string table\n"));
status = B_ERROR;
goto error3;
}
TRACE(("loaded %" B_PRId32 " debug symbols\n", numSymbols));
image->debug_symbols = symbolTable;
image->num_debug_symbols = numSymbols;
image->debug_string_table = stringTable;
free(sectionHeaders);
return B_OK;
error3:
free(stringTable);
error2:
free(symbolTable);
error1:
free(sectionHeaders);
return status;
}
static status_t
insert_preloaded_image(preloaded_elf_image *preloadedImage, bool kernel)
{
status_t status;
status = verify_eheader(&preloadedImage->elf_header);
if (status != B_OK)
return status;
elf_image_info *image = create_image_struct();
if (image == NULL)
return B_NO_MEMORY;
image->name = strdup(preloadedImage->name);
image->dynamic_section = preloadedImage->dynamic_section.start;
image->text_region.id = preloadedImage->text_region.id;
image->text_region.start = preloadedImage->text_region.start;
image->text_region.size = preloadedImage->text_region.size;
image->text_region.delta = preloadedImage->text_region.delta;
image->data_region.id = preloadedImage->data_region.id;
image->data_region.start = preloadedImage->data_region.start;
image->data_region.size = preloadedImage->data_region.size;
image->data_region.delta = preloadedImage->data_region.delta;
status = elf_parse_dynamic_section(image);
if (status != B_OK)
goto error1;
status = init_image_version_infos(image);
if (status != B_OK)
goto error1;
if (!kernel) {
status = check_needed_image_versions(image);
if (status != B_OK)
goto error1;
status = elf_relocate(image, sKernelImage);
if (status != B_OK)
goto error1;
} else
sKernelImage = image;
if (preloadedImage->debug_symbols != NULL) {
int32 debugSymbolsSize = sizeof(elf_sym)
* preloadedImage->num_debug_symbols;
image->debug_symbols = (elf_sym*)malloc(debugSymbolsSize);
if (image->debug_symbols != NULL) {
memcpy(image->debug_symbols, preloadedImage->debug_symbols,
debugSymbolsSize);
}
}
image->num_debug_symbols = preloadedImage->num_debug_symbols;
if (preloadedImage->debug_string_table != NULL) {
image->debug_string_table = (char*)malloc(
preloadedImage->debug_string_table_size);
if (image->debug_string_table != NULL) {
memcpy((void*)image->debug_string_table,
preloadedImage->debug_string_table,
preloadedImage->debug_string_table_size);
}
}
register_elf_image(image);
preloadedImage->id = image->id;
set_area_protection(image->text_region.id,
B_KERNEL_READ_AREA | B_KERNEL_EXECUTE_AREA);
return B_OK;
error1:
delete_elf_image(image);
preloadedImage->id = -1;
return status;
}
class UserSymbolLookup {
public:
static UserSymbolLookup& Default()
{
return sLookup;
}
status_t Init(Team* team)
{
fTeam = team;
return B_OK;
}
status_t LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
const char **_symbolName, const char **_imageName, bool *_exactMatch)
{
struct image *image;
status_t error = _FindImageAtAddress(address, image);
if (error != B_OK)
return error;
if (image->info.basic_info.text == fTeam->commpage_address) {
if (*_imageName != NULL)
*_imageName = "commpage";
address -= (addr_t)fTeam->commpage_address;
error = elf_debug_lookup_symbol_address(address, _baseAddress,
_symbolName, NULL, _exactMatch);
if (_baseAddress != NULL)
*_baseAddress += (addr_t)fTeam->commpage_address;
return error;
}
const extended_image_info& info = image->info;
const uint32 *symhash = (uint32 *)info.symbol_hash;
elf_sym *syms = (elf_sym *)info.symbol_table;
strlcpy(fImageName, info.basic_info.name, sizeof(fImageName));
uint32 hashTabSize;
if (!_Read(symhash, hashTabSize))
return B_BAD_ADDRESS;
const uint32* hashBuckets = symhash + 2;
const uint32* hashChains = symhash + 2 + hashTabSize;
const addr_t loadDelta = (addr_t)info.basic_info.text;
elf_sym symbolFound;
addr_t deltaFound = INT_MAX;
bool exactMatch = false;
symbolFound.st_name = 0;
symbolFound.st_value = 0;
for (uint32 i = 0; i < hashTabSize; i++) {
uint32 bucket;
if (!_Read(&hashBuckets[i], bucket))
return B_BAD_ADDRESS;
for (uint32 j = bucket; j != STN_UNDEF;
_Read(&hashChains[j], j) ? 0 : j = STN_UNDEF) {
elf_sym symbol;
if (!_Read(syms + j, symbol))
continue;
if ((symbol.Type() != STT_FUNC && symbol.Type() != STT_OBJECT)
|| symbol.st_value == 0
|| (symbol.st_value + symbol.st_size) > (elf_addr)info.basic_info.text_size) {
continue;
}
addr_t symbolAddress = symbol.st_value + loadDelta;
if (symbolAddress > address)
continue;
addr_t symbolDelta = address - symbolAddress;
if (symbolDelta < deltaFound) {
deltaFound = symbolDelta;
symbolFound = symbol;
if (symbolDelta >= 0 && symbolDelta < symbol.st_size) {
exactMatch = true;
break;
}
}
}
}
if (_imageName)
*_imageName = fImageName;
if (_symbolName) {
*_symbolName = NULL;
if (deltaFound < INT_MAX) {
if (_ReadString(info, symbolFound.st_name, fSymbolName,
sizeof(fSymbolName))) {
*_symbolName = fSymbolName;
} else {
deltaFound = INT_MAX;
}
}
}
if (_baseAddress) {
if (deltaFound < INT_MAX)
*_baseAddress = symbolFound.st_value + loadDelta;
else
*_baseAddress = loadDelta;
}
if (_exactMatch)
*_exactMatch = exactMatch;
return B_OK;
}
status_t _FindImageAtAddress(addr_t address, struct image*& _image)
{
for (struct image* image = fTeam->image_list.First();
image != NULL; image = fTeam->image_list.GetNext(image)) {
image_info *info = &image->info.basic_info;
if ((address < (addr_t)info->text
|| address >= (addr_t)info->text + info->text_size)
&& (address < (addr_t)info->data
|| address >= (addr_t)info->data + info->data_size))
continue;
_image = image;
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
bool _ReadString(const extended_image_info& info, uint32 offset, char* buffer,
size_t bufferSize)
{
const char* address = (char *)info.string_table + offset;
if (!IS_USER_ADDRESS(address))
return false;
if (debug_debugger_running()) {
return debug_strlcpy(B_CURRENT_TEAM, buffer, address, bufferSize)
>= 0;
}
return user_strlcpy(buffer, address, bufferSize) >= 0;
}
template<typename T> bool _Read(const T* address, T& data);
private:
Team* fTeam;
char fImageName[B_OS_NAME_LENGTH];
char fSymbolName[256];
static UserSymbolLookup sLookup;
};
template<typename T>
bool
UserSymbolLookup::_Read(const T* address, T& data)
{
if (!IS_USER_ADDRESS(address))
return false;
if (debug_debugger_running())
return debug_memcpy(B_CURRENT_TEAM, &data, address, sizeof(T)) == B_OK;
return user_memcpy(&data, address, sizeof(T)) == B_OK;
}
UserSymbolLookup UserSymbolLookup::sLookup;
status_t
get_image_symbol(image_id id, const char *name, int32 symbolClass,
void **_symbol)
{
struct elf_image_info *image;
elf_sym *symbol;
status_t status = B_OK;
TRACE(("get_image_symbol(%s)\n", name));
mutex_lock(&sImageMutex);
image = find_image(id);
if (image == NULL) {
status = B_BAD_IMAGE_ID;
goto done;
}
symbol = elf_find_symbol(image, name, NULL, true);
if (symbol == NULL || symbol->st_shndx == SHN_UNDEF) {
status = B_ENTRY_NOT_FOUND;
goto done;
}
TRACE(("found: %lx (%lx + %lx)\n",
symbol->st_value + image->text_region.delta,
symbol->st_value, image->text_region.delta));
*_symbol = (void *)(symbol->st_value + image->text_region.delta);
done:
mutex_unlock(&sImageMutex);
return status;
}
status_t
elf_debug_lookup_symbol_address(addr_t address, addr_t *_baseAddress,
const char **_symbolName, const char **_imageName, bool *_exactMatch)
{
struct elf_image_info *image;
elf_sym *symbolFound = NULL;
const char *symbolName = NULL;
addr_t deltaFound = INT_MAX;
bool exactMatch = false;
status_t status;
TRACE(("looking up %p\n", (void *)address));
if (!sInitialized)
return B_ERROR;
image = find_image_at_address(address);
if (image != NULL) {
addr_t symbolDelta;
uint32 i;
int32 j;
TRACE((" image %p, base = %p, size = %p\n", image,
(void *)image->text_region.start, (void *)image->text_region.size));
if (image->debug_symbols != NULL) {
TRACE((" searching debug symbols...\n"));
for (i = 0; i < image->num_debug_symbols; i++) {
elf_sym *symbol = &image->debug_symbols[i];
if (symbol->st_value == 0 || symbol->st_size
>= image->text_region.size + image->data_region.size)
continue;
symbolDelta
= address - (symbol->st_value + image->text_region.delta);
if (symbolDelta >= 0 && symbolDelta < symbol->st_size)
exactMatch = true;
if (exactMatch || symbolDelta < deltaFound) {
deltaFound = symbolDelta;
symbolFound = symbol;
symbolName = image->debug_string_table + symbol->st_name;
if (exactMatch)
break;
}
}
} else {
TRACE((" searching standard symbols...\n"));
for (i = 0; i < HASHTABSIZE(image); i++) {
for (j = HASHBUCKETS(image)[i]; j != STN_UNDEF;
j = HASHCHAINS(image)[j]) {
elf_sym *symbol = &image->syms[j];
if (symbol->st_value == 0
|| symbol->st_size >= image->text_region.size
+ image->data_region.size)
continue;
symbolDelta = address - (long)(symbol->st_value
+ image->text_region.delta);
if (symbolDelta >= 0 && symbolDelta < symbol->st_size)
exactMatch = true;
if (exactMatch || symbolDelta < deltaFound) {
deltaFound = symbolDelta;
symbolFound = symbol;
symbolName = SYMNAME(image, symbol);
if (exactMatch)
goto symbol_found;
}
}
}
}
}
symbol_found:
if (symbolFound != NULL) {
if (_symbolName)
*_symbolName = symbolName;
if (_imageName)
*_imageName = image->name;
if (_baseAddress)
*_baseAddress = symbolFound->st_value + image->text_region.delta;
if (_exactMatch)
*_exactMatch = exactMatch;
status = B_OK;
} else if (image != NULL) {
TRACE(("symbol not found!\n"));
if (_symbolName)
*_symbolName = NULL;
if (_imageName)
*_imageName = image->name;
if (_baseAddress)
*_baseAddress = image->text_region.start;
if (_exactMatch)
*_exactMatch = false;
status = B_OK;
} else {
TRACE(("image not found!\n"));
status = B_ENTRY_NOT_FOUND;
}
return status;
}
status_t
elf_debug_lookup_user_symbol_address(Team* team, addr_t address,
addr_t *_baseAddress, const char **_symbolName, const char **_imageName,
bool *_exactMatch)
{
if (team == NULL || team == team_get_kernel_team())
return B_BAD_VALUE;
UserSymbolLookup& lookup = UserSymbolLookup::Default();
status_t error = lookup.Init(team);
if (error != B_OK)
return error;
return lookup.LookupSymbolAddress(address, _baseAddress, _symbolName,
_imageName, _exactMatch);
}
addr_t
elf_debug_lookup_symbol(const char* searchName)
{
struct elf_image_info *image = NULL;
ImageHash::Iterator iterator(sImagesHash);
while (iterator.HasNext()) {
image = iterator.Next();
if (image->num_debug_symbols > 0) {
for (uint32 i = 0; i < image->num_debug_symbols; i++) {
elf_sym *symbol = &image->debug_symbols[i];
const char *name = image->debug_string_table + symbol->st_name;
if (symbol->st_value > 0 && !strcmp(name, searchName))
return symbol->st_value + image->text_region.delta;
}
} else {
for (uint32 i = 0; i < HASHTABSIZE(image); i++) {
for (uint32 j = HASHBUCKETS(image)[i]; j != STN_UNDEF;
j = HASHCHAINS(image)[j]) {
elf_sym *symbol = &image->syms[j];
const char *name = SYMNAME(image, symbol);
if (symbol->st_value > 0 && !strcmp(name, searchName))
return symbol->st_value + image->text_region.delta;
}
}
}
}
return 0;
}
status_t
elf_lookup_kernel_symbol(const char* name, elf_symbol_info* info)
{
elf_sym* foundSymbol = elf_find_symbol(sKernelImage, name, NULL, false);
if (foundSymbol == NULL)
return B_MISSING_SYMBOL;
info->address = foundSymbol->st_value + sKernelImage->text_region.delta;
info->size = foundSymbol->st_size;
return B_OK;
}
#endif
status_t
elf_load_user_image(const char *path, Team *team, uint32 flags, addr_t *entry)
{
elf_ehdr elfHeader;
char baseName[B_OS_NAME_LENGTH];
status_t status;
ssize_t length;
TRACE(("elf_load: entry path '%s', team %p\n", path, team));
int fd = _kern_open(-1, path, O_RDONLY, 0);
if (fd < 0)
return fd;
FileDescriptorCloser fdCloser(fd);
struct stat st;
status = _kern_read_stat(fd, NULL, false, &st, sizeof(st));
if (status != B_OK)
return status;
length = _kern_read(fd, 0, &elfHeader, sizeof(elfHeader));
if (length < B_OK)
return length;
if (length != sizeof(elfHeader)) {
return B_NOT_AN_EXECUTABLE;
}
status = verify_eheader(&elfHeader);
if (status < B_OK)
return status;
#ifdef ELF_LOAD_USER_IMAGE_TEST_EXECUTABLE
if ((flags & ELF_LOAD_USER_IMAGE_TEST_EXECUTABLE) != 0)
return B_OK;
#endif
struct elf_image_info* image;
image = create_image_struct();
if (image == NULL)
return B_NO_MEMORY;
CObjectDeleter<elf_image_info, void, delete_elf_image> imageDeleter(image);
struct ElfHeaderUnsetter {
ElfHeaderUnsetter(elf_image_info* image)
: fImage(image)
{
}
~ElfHeaderUnsetter()
{
fImage->elf_header = NULL;
}
elf_image_info* fImage;
} headerUnsetter(image);
image->elf_header = &elfHeader;
elf_phdr *programHeaders = (elf_phdr *)malloc(
elfHeader.e_phnum * elfHeader.e_phentsize);
if (programHeaders == NULL) {
dprintf("error allocating space for program headers\n");
return B_NO_MEMORY;
}
MemoryDeleter headersDeleter(programHeaders);
TRACE(("reading in program headers at 0x%lx, length 0x%x\n",
elfHeader.e_phoff, elfHeader.e_phnum * elfHeader.e_phentsize));
length = _kern_read(fd, elfHeader.e_phoff, programHeaders,
elfHeader.e_phnum * elfHeader.e_phentsize);
if (length < B_OK) {
dprintf("error reading in program headers\n");
return length;
}
if (length != elfHeader.e_phnum * elfHeader.e_phentsize) {
dprintf("short read while reading in program headers\n");
return B_ERROR;
}
{
int32 length;
const char *leaf = strrchr(path, '/');
if (leaf == NULL)
leaf = path;
else
leaf++;
length = strlen(leaf);
if (length > B_OS_NAME_LENGTH - 16)
snprintf(baseName, B_OS_NAME_LENGTH, "...%s", leaf + length + 16 - B_OS_NAME_LENGTH);
else
strcpy(baseName, leaf);
}
BStackOrHeapArray<area_id, 8> mappedAreas(elfHeader.e_phnum);
if (!mappedAreas.IsValid())
return B_NO_MEMORY;
extended_image_info imageInfo;
memset(&imageInfo, 0, sizeof(imageInfo));
addr_t delta = 0;
uint32 addressSpec = B_RANDOMIZED_BASE_ADDRESS;
for (int i = 0; i < elfHeader.e_phnum; i++) {
char regionName[B_OS_NAME_LENGTH];
char *regionAddress;
char *originalRegionAddress;
area_id id;
mappedAreas[i] = -1;
if (programHeaders[i].p_type == PT_DYNAMIC) {
image->dynamic_section = programHeaders[i].p_vaddr;
continue;
}
if (programHeaders[i].p_type != PT_LOAD)
continue;
regionAddress = (char *)(ROUNDDOWN(programHeaders[i].p_vaddr,
B_PAGE_SIZE) + delta);
originalRegionAddress = regionAddress;
if (programHeaders[i].p_flags & PF_WRITE) {
size_t memUpperBound = (programHeaders[i].p_vaddr % B_PAGE_SIZE)
+ programHeaders[i].p_memsz;
size_t fileUpperBound = (programHeaders[i].p_vaddr % B_PAGE_SIZE)
+ programHeaders[i].p_filesz;
memUpperBound = ROUNDUP(memUpperBound, B_PAGE_SIZE);
fileUpperBound = ROUNDUP(fileUpperBound, B_PAGE_SIZE);
snprintf(regionName, B_OS_NAME_LENGTH, "%s_seg%drw", baseName, i);
id = vm_map_file(team->id, regionName, (void **)®ionAddress,
addressSpec, fileUpperBound,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
REGION_PRIVATE_MAP, false, fd,
ROUNDDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
if (id < B_OK) {
dprintf("error mapping file data: %s!\n", strerror(id));
return B_NOT_AN_EXECUTABLE;
}
mappedAreas[i] = id;
imageInfo.basic_info.data = regionAddress;
imageInfo.basic_info.data_size = memUpperBound;
image->data_region.start = (addr_t)regionAddress;
image->data_region.size = memUpperBound;
addr_t start = (addr_t)regionAddress
+ (programHeaders[i].p_vaddr % B_PAGE_SIZE)
+ programHeaders[i].p_filesz;
size_t amount = fileUpperBound
- (programHeaders[i].p_vaddr % B_PAGE_SIZE)
- (programHeaders[i].p_filesz);
memset((void *)start, 0, amount);
if (memUpperBound != fileUpperBound) {
size_t bssSize = memUpperBound - fileUpperBound;
snprintf(regionName, B_OS_NAME_LENGTH, "%s_bss%d", baseName, i);
regionAddress += fileUpperBound;
virtual_address_restrictions virtualRestrictions = {};
virtualRestrictions.address = regionAddress;
virtualRestrictions.address_specification = B_EXACT_ADDRESS;
physical_address_restrictions physicalRestrictions = {};
id = create_area_etc(team->id, regionName, bssSize, B_NO_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0, 0, &virtualRestrictions,
&physicalRestrictions, (void**)®ionAddress);
if (id < B_OK) {
dprintf("error allocating bss area: %s!\n", strerror(id));
return B_NOT_AN_EXECUTABLE;
}
}
} else {
snprintf(regionName, B_OS_NAME_LENGTH, "%s_seg%drx", baseName, i);
size_t segmentSize = ROUNDUP(programHeaders[i].p_memsz
+ (programHeaders[i].p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE);
id = vm_map_file(team->id, regionName, (void **)®ionAddress,
addressSpec, segmentSize,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
REGION_PRIVATE_MAP, false, fd,
ROUNDDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
if (id < B_OK) {
dprintf("error mapping file text: %s!\n", strerror(id));
return B_NOT_AN_EXECUTABLE;
}
mappedAreas[i] = id;
imageInfo.basic_info.text = regionAddress;
imageInfo.basic_info.text_size = segmentSize;
image->text_region.start = (addr_t)regionAddress;
image->text_region.size = segmentSize;
}
if (addressSpec != B_EXACT_ADDRESS) {
addressSpec = B_EXACT_ADDRESS;
delta = regionAddress - originalRegionAddress;
}
}
image->data_region.delta = delta;
image->text_region.delta = delta;
image->dynamic_section += image->text_region.delta;
status = elf_parse_dynamic_section(image);
if (status != B_OK)
return status;
status = elf_relocate(image, image);
if (status != B_OK)
return status;
for (int i = 0; i < elfHeader.e_phnum; i++) {
if (mappedAreas[i] == -1)
continue;
uint32 protection = 0;
if (programHeaders[i].p_flags & PF_EXECUTE)
protection |= B_EXECUTE_AREA;
if (programHeaders[i].p_flags & PF_WRITE)
protection |= B_WRITE_AREA;
if (programHeaders[i].p_flags & PF_READ)
protection |= B_READ_AREA;
status = vm_set_area_protection(mappedAreas[i], protection,
true);
if (status != B_OK)
return status;
}
imageInfo.basic_info.type = B_LIBRARY_IMAGE;
imageInfo.basic_info.device = st.st_dev;
imageInfo.basic_info.node = st.st_ino;
strlcpy(imageInfo.basic_info.name, path, sizeof(imageInfo.basic_info.name));
imageInfo.basic_info.api_version = B_HAIKU_VERSION;
imageInfo.basic_info.abi = B_HAIKU_ABI;
imageInfo.text_delta = delta;
imageInfo.symbol_table = image->syms;
imageInfo.symbol_hash = image->symhash;
imageInfo.string_table = image->strtab;
imageInfo.basic_info.id = register_image(team, &imageInfo,
sizeof(imageInfo));
if (imageInfo.basic_info.id >= 0 && team_get_current_team_id() == team->id)
user_debug_image_created(&imageInfo.basic_info);
TRACE(("elf_load: done!\n"));
*entry = elfHeader.e_entry + delta;
return B_OK;
}
#ifndef ELF32_COMPAT
image_id
load_kernel_add_on(const char *path)
{
elf_phdr *programHeaders;
elf_ehdr *elfHeader;
struct elf_image_info *image;
const char *fileName;
void *reservedAddress;
size_t reservedSize;
status_t status;
ssize_t length;
bool textSectionWritable = false;
int executableHeaderCount = 0;
TRACE(("elf_load_kspace: entry path '%s'\n", path));
int fd = _kern_open(-1, path, O_RDONLY, 0);
if (fd < 0)
return fd;
struct vnode *vnode;
status = vfs_get_vnode_from_fd(fd, true, &vnode);
if (status < B_OK)
goto error0;
fileName = strrchr(path, '/');
if (fileName == NULL)
fileName = path;
else
fileName++;
mutex_lock(&sImageLoadMutex);
image = find_image_by_vnode(vnode);
if (image) {
atomic_add(&image->ref_count, 1);
goto done;
}
elfHeader = (elf_ehdr *)malloc(sizeof(*elfHeader));
if (!elfHeader) {
status = B_NO_MEMORY;
goto error;
}
length = _kern_read(fd, 0, elfHeader, sizeof(*elfHeader));
if (length < B_OK) {
status = length;
goto error1;
}
if (length != sizeof(*elfHeader)) {
status = B_NOT_AN_EXECUTABLE;
goto error1;
}
status = verify_eheader(elfHeader);
if (status < B_OK)
goto error1;
image = create_image_struct();
if (!image) {
status = B_NO_MEMORY;
goto error1;
}
image->vnode = vnode;
image->elf_header = elfHeader;
image->name = strdup(path);
vnode = NULL;
programHeaders = (elf_phdr *)malloc(elfHeader->e_phnum
* elfHeader->e_phentsize);
if (programHeaders == NULL) {
dprintf("%s: error allocating space for program headers\n", fileName);
status = B_NO_MEMORY;
goto error2;
}
TRACE(("reading in program headers at 0x%lx, length 0x%x\n",
elfHeader->e_phoff, elfHeader->e_phnum * elfHeader->e_phentsize));
length = _kern_read(fd, elfHeader->e_phoff, programHeaders,
elfHeader->e_phnum * elfHeader->e_phentsize);
if (length < B_OK) {
status = length;
TRACE(("%s: error reading in program headers\n", fileName));
goto error3;
}
if (length != elfHeader->e_phnum * elfHeader->e_phentsize) {
TRACE(("%s: short read while reading in program headers\n", fileName));
status = B_ERROR;
goto error3;
}
reservedSize = 0;
length = 0;
for (int32 i = 0; i < elfHeader->e_phnum; i++) {
size_t end;
if (programHeaders[i].p_type != PT_LOAD)
continue;
length += ROUNDUP(programHeaders[i].p_memsz
+ (programHeaders[i].p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE);
end = ROUNDUP(programHeaders[i].p_memsz + programHeaders[i].p_vaddr,
B_PAGE_SIZE);
if (end > reservedSize)
reservedSize = end;
if (programHeaders[i].IsExecutable())
executableHeaderCount++;
}
if ((ssize_t)reservedSize > length + 8 * 1024) {
status = B_BAD_DATA;
goto error1;
}
if (vm_reserve_address_range(VMAddressSpace::KernelID(), &reservedAddress,
B_ANY_KERNEL_ADDRESS, reservedSize, 0) < B_OK) {
status = B_NO_MEMORY;
goto error3;
}
image->data_region.size = 0;
image->text_region.size = 0;
for (int32 i = 0; i < elfHeader->e_phnum; i++) {
char regionName[B_OS_NAME_LENGTH];
elf_region *region;
TRACE(("looking at program header %" B_PRId32 "\n", i));
switch (programHeaders[i].p_type) {
case PT_LOAD:
break;
case PT_DYNAMIC:
image->dynamic_section = programHeaders[i].p_vaddr;
continue;
case PT_INTERP:
continue;
case PT_PHDR:
case PT_STACK:
continue;
case PT_EH_FRAME:
continue;
case PT_ARM_UNWIND:
continue;
case PT_RISCV_ATTRIBUTES:
continue;
default:
dprintf("%s: unhandled pheader type %#" B_PRIx32 "\n", fileName,
programHeaders[i].p_type);
continue;
}
if (programHeaders[i].IsReadWrite()
&& (!programHeaders[i].IsExecutable()
|| executableHeaderCount > 1)) {
if (image->data_region.size != 0) {
continue;
}
region = &image->data_region;
snprintf(regionName, B_OS_NAME_LENGTH, "%s_data", fileName);
} else if (programHeaders[i].IsExecutable()) {
if (image->text_region.size != 0) {
continue;
}
region = &image->text_region;
textSectionWritable = programHeaders[i].IsReadWrite();
snprintf(regionName, B_OS_NAME_LENGTH, "%s_text", fileName);
} else {
dprintf("%s: weird program header flags %#" B_PRIx32 "\n", fileName,
programHeaders[i].p_flags);
continue;
}
region->start = (addr_t)reservedAddress + ROUNDDOWN(
programHeaders[i].p_vaddr, B_PAGE_SIZE);
region->size = ROUNDUP(programHeaders[i].p_memsz
+ (programHeaders[i].p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE);
region->id = create_area(regionName, (void **)®ion->start,
B_EXACT_ADDRESS, region->size, B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (region->id < B_OK) {
dprintf("%s: error allocating area: %s\n", fileName,
strerror(region->id));
status = B_NOT_AN_EXECUTABLE;
goto error4;
}
region->delta = -ROUNDDOWN(programHeaders[i].p_vaddr, B_PAGE_SIZE);
TRACE(("elf_load_kspace: created area \"%s\" at %p\n",
regionName, (void *)region->start));
length = _kern_read(fd, programHeaders[i].p_offset,
(void *)(region->start + (programHeaders[i].p_vaddr % B_PAGE_SIZE)),
programHeaders[i].p_filesz);
if (length < B_OK) {
status = length;
dprintf("%s: error reading in segment %" B_PRId32 "\n", fileName,
i);
goto error5;
}
}
image->data_region.delta += image->data_region.start;
image->text_region.delta += image->text_region.start;
image->dynamic_section += image->text_region.delta;
status = elf_parse_dynamic_section(image);
if (status < B_OK)
goto error5;
status = init_image_version_infos(image);
if (status != B_OK)
goto error5;
status = check_needed_image_versions(image);
if (status != B_OK)
goto error5;
status = elf_relocate(image, sKernelImage);
if (status < B_OK)
goto error5;
set_area_protection(image->text_region.id,
B_KERNEL_READ_AREA | B_KERNEL_EXECUTE_AREA
| (textSectionWritable ? B_KERNEL_WRITE_AREA : 0));
vm_unreserve_address_range(VMAddressSpace::KernelID(), reservedAddress,
reservedSize);
if (sLoadElfSymbols)
load_elf_symbol_table(fd, image);
free(programHeaders);
mutex_lock(&sImageMutex);
register_elf_image(image);
mutex_unlock(&sImageMutex);
done:
_kern_close(fd);
mutex_unlock(&sImageLoadMutex);
return image->id;
error5:
error4:
vm_unreserve_address_range(VMAddressSpace::KernelID(), reservedAddress,
reservedSize);
error3:
free(programHeaders);
error2:
delete_elf_image(image);
elfHeader = NULL;
error1:
free(elfHeader);
error:
mutex_unlock(&sImageLoadMutex);
error0:
dprintf("Could not load kernel add-on \"%s\": %s\n", path,
strerror(status));
if (vnode)
vfs_put_vnode(vnode);
_kern_close(fd);
return status;
}
status_t
unload_kernel_add_on(image_id id)
{
MutexLocker _(sImageLoadMutex);
MutexLocker _2(sImageMutex);
elf_image_info *image = find_image(id);
if (image == NULL)
return B_BAD_IMAGE_ID;
unload_elf_image(image);
return B_OK;
}
struct elf_image_info*
elf_get_kernel_image()
{
return sKernelImage;
}
status_t
elf_get_image_info_for_address(addr_t address, image_info* info)
{
MutexLocker _(sImageMutex);
struct elf_image_info* elfInfo = find_image_at_address(address);
if (elfInfo == NULL)
return B_ENTRY_NOT_FOUND;
info->id = elfInfo->id;
info->type = B_SYSTEM_IMAGE;
info->sequence = 0;
info->init_order = 0;
info->init_routine = NULL;
info->term_routine = NULL;
info->device = -1;
info->node = -1;
strlcpy(info->name, elfInfo->name, sizeof(info->name));
info->text = (void*)elfInfo->text_region.start;
info->data = (void*)elfInfo->data_region.start;
info->text_size = elfInfo->text_region.size;
info->data_size = elfInfo->data_region.size;
return B_OK;
}
image_id
elf_create_memory_image(const char* imageName, addr_t text, size_t textSize,
addr_t data, size_t dataSize)
{
elf_image_info* image = create_image_struct();
if (image == NULL)
return B_NO_MEMORY;
MemoryDeleter imageDeleter(image);
elf_sym* symbolTable = (elf_sym*)malloc(0);
char* stringTable = (char*)malloc(1);
MemoryDeleter symbolTableDeleter(symbolTable);
MemoryDeleter stringTableDeleter(stringTable);
if (symbolTable == NULL || stringTable == NULL)
return B_NO_MEMORY;
stringTable[0] = '\0';
image->debug_symbols = symbolTable;
image->num_debug_symbols = 0;
image->debug_string_table = stringTable;
image->name = strdup(imageName);
if (image->name == NULL)
return B_NO_MEMORY;
image->text_region.id = -1;
image->text_region.start = text;
image->text_region.size = textSize;
image->text_region.delta = 0;
image->data_region.id = -1;
image->data_region.start = data;
image->data_region.size = dataSize;
image->data_region.delta = 0;
mutex_lock(&sImageMutex);
register_elf_image(image);
image_id imageID = image->id;
mutex_unlock(&sImageMutex);
imageDeleter.Detach();
symbolTableDeleter.Detach();
stringTableDeleter.Detach();
return imageID;
}
status_t
elf_add_memory_image_symbol(image_id id, const char* name, addr_t address,
size_t size, int32 type)
{
MutexLocker _(sImageMutex);
struct elf_image_info* image = find_image(id);
if (image == NULL)
return B_ENTRY_NOT_FOUND;
size_t stringTableSize = 1;
if (image->num_debug_symbols > 0) {
for (int32 i = image->num_debug_symbols - 1; i >= 0; i--) {
int32 nameIndex = image->debug_symbols[i].st_name;
if (nameIndex != 0) {
stringTableSize = nameIndex
+ strlen(image->debug_string_table + nameIndex) + 1;
break;
}
}
}
char* stringTable = (char*)image->debug_string_table;
size_t stringIndex = 0;
if (name != NULL) {
size_t nameSize = strlen(name) + 1;
stringIndex = stringTableSize;
stringTableSize += nameSize;
stringTable = (char*)realloc((char*)image->debug_string_table,
stringTableSize);
if (stringTable == NULL)
return B_NO_MEMORY;
image->debug_string_table = stringTable;
memcpy(stringTable + stringIndex, name, nameSize);
}
int32 symbolCount = image->num_debug_symbols + 1;
elf_sym* symbolTable = (elf_sym*)realloc(
(elf_sym*)image->debug_symbols, sizeof(elf_sym) * symbolCount);
if (symbolTable == NULL)
return B_NO_MEMORY;
image->debug_symbols = symbolTable;
elf_sym& symbol = symbolTable[symbolCount - 1];
symbol.SetInfo(STB_GLOBAL,
type == B_SYMBOL_TYPE_DATA ? STT_OBJECT : STT_FUNC);
symbol.st_name = stringIndex;
symbol.st_value = address;
symbol.st_size = size;
symbol.st_other = 0;
symbol.st_shndx = 0;
image->num_debug_symbols++;
return B_OK;
}
status_t
elf_read_kernel_image_symbols(image_id id, elf_sym* symbolTable,
int32* _symbolCount, char* stringTable, size_t* _stringTableSize,
addr_t* _imageDelta, bool kernel)
{
if (_symbolCount == NULL || _stringTableSize == NULL)
return B_BAD_VALUE;
if (!kernel) {
if (!IS_USER_ADDRESS(_symbolCount) || !IS_USER_ADDRESS(_stringTableSize)
|| (_imageDelta != NULL && !IS_USER_ADDRESS(_imageDelta))
|| (symbolTable != NULL && !IS_USER_ADDRESS(symbolTable))
|| (stringTable != NULL && !IS_USER_ADDRESS(stringTable))) {
return B_BAD_ADDRESS;
}
}
int32 maxSymbolCount;
size_t maxStringTableSize;
if (kernel) {
maxSymbolCount = *_symbolCount;
maxStringTableSize = *_stringTableSize;
} else {
if (user_memcpy(&maxSymbolCount, _symbolCount, sizeof(maxSymbolCount))
!= B_OK
|| user_memcpy(&maxStringTableSize, _stringTableSize,
sizeof(maxStringTableSize)) != B_OK) {
return B_BAD_ADDRESS;
}
}
MutexLocker _(sImageMutex);
struct elf_image_info* image = find_image(id);
if (image == NULL)
return B_ENTRY_NOT_FOUND;
addr_t imageDelta = image->text_region.delta;
const elf_sym* symbols;
int32 symbolCount;
const char* strings;
if (image->debug_symbols != NULL) {
symbols = image->debug_symbols;
symbolCount = image->num_debug_symbols;
strings = image->debug_string_table;
} else {
symbols = image->syms;
symbolCount = image->symhash[1];
strings = image->strtab;
}
size_t stringTableSize = 0;
for (int32 i = 0; i < symbolCount; i++) {
size_t index = symbols[i].st_name;
if (index > stringTableSize)
stringTableSize = index;
}
stringTableSize += strlen(strings + stringTableSize) + 1;
int32 symbolsToCopy = min_c(symbolCount, maxSymbolCount);
if (symbolTable != NULL && symbolsToCopy > 0) {
if (kernel) {
memcpy(symbolTable, symbols, sizeof(elf_sym) * symbolsToCopy);
} else if (user_memcpy(symbolTable, symbols,
sizeof(elf_sym) * symbolsToCopy) != B_OK) {
return B_BAD_ADDRESS;
}
}
size_t stringsToCopy = min_c(stringTableSize, maxStringTableSize);
if (stringTable != NULL && stringsToCopy > 0) {
if (kernel) {
memcpy(stringTable, strings, stringsToCopy);
} else {
if (user_memcpy(stringTable, strings, stringsToCopy)
!= B_OK) {
return B_BAD_ADDRESS;
}
}
}
if (kernel) {
*_symbolCount = symbolCount;
*_stringTableSize = stringTableSize;
if (_imageDelta != NULL)
*_imageDelta = imageDelta;
} else {
if (user_memcpy(_symbolCount, &symbolCount, sizeof(symbolCount)) != B_OK
|| user_memcpy(_stringTableSize, &stringTableSize,
sizeof(stringTableSize)) != B_OK
|| (_imageDelta != NULL && user_memcpy(_imageDelta, &imageDelta,
sizeof(imageDelta)) != B_OK)) {
return B_BAD_ADDRESS;
}
}
return B_OK;
}
status_t
elf_init(kernel_args* args)
{
struct preloaded_image* image;
image_init();
if (void* handle = load_driver_settings("kernel")) {
sLoadElfSymbols = get_driver_boolean_parameter(handle, "load_symbols",
false, false);
unload_driver_settings(handle);
}
sImagesHash = new(std::nothrow) ImageHash();
if (sImagesHash == NULL)
return B_NO_MEMORY;
status_t init = sImagesHash->Init(IMAGE_HASH_SIZE);
if (init != B_OK)
return init;
image = args->kernel_image;
if (insert_preloaded_image(static_cast<preloaded_elf_image *>(image),
true) < B_OK)
panic("could not create kernel image.\n");
for (image = args->preloaded_images; image != NULL; image = image->next)
insert_preloaded_image(static_cast<preloaded_elf_image *>(image),
false);
add_debugger_command("ls", &dump_address_info,
"lookup symbol for a particular address");
add_debugger_command("symbols", &dump_symbols, "dump symbols for image");
add_debugger_command("symbol", &dump_symbol, "search symbol in images");
add_debugger_command_etc("image", &dump_image, "dump image info",
"Prints info about the specified image.\n"
" <image> - pointer to the semaphore structure, or ID\n"
" of the image to print info for.\n", 0);
sInitialized = true;
return B_OK;
}
status_t
_user_read_kernel_image_symbols(image_id id, elf_sym* symbolTable,
int32* _symbolCount, char* stringTable, size_t* _stringTableSize,
addr_t* _imageDelta)
{
return elf_read_kernel_image_symbols(id, symbolTable, _symbolCount,
stringTable, _stringTableSize, _imageDelta, false);
}
#endif