#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ElfImage.h"
#include "ElfSymbolPatcher.h"
class ElfSymbolPatchInfo::Entry {
public:
static Entry* Create(image_id image, void*** targets,
int32 targetCount);
void Delete();
image_id GetImage() const { return fImage; }
void Patch(void* newAddress);
private:
Entry();
Entry(const Entry&);
Entry(image_id image, void*** targets,
int32 targetCount);
~Entry();
private:
image_id fImage;
int32 fPatchTargetCount;
void** fPatchTargets[1];
};
ElfSymbolPatchInfo::Entry*
ElfSymbolPatchInfo::Entry::Create(image_id image, void*** targets,
int32 targetCount)
{
if (!targets || targetCount <= 0)
return NULL;
void* buffer = malloc(sizeof(Entry) + sizeof(void**) * (targetCount - 1));
Entry* entry = NULL;
if (buffer)
entry = new(buffer) Entry(image, targets, targetCount);
return entry;
}
void
ElfSymbolPatchInfo::Entry::Delete()
{
this->~Entry();
free(this);
}
void
ElfSymbolPatchInfo::Entry::Patch(void* newAddress)
{
for (int i = 0; i < fPatchTargetCount; i++)
*fPatchTargets[i] = newAddress;
}
ElfSymbolPatchInfo::Entry::Entry(image_id image, void*** targets,
int32 targetCount)
: fImage(image),
fPatchTargetCount(targetCount)
{
memcpy(fPatchTargets + 0, targets, targetCount * sizeof(void**));
}
ElfSymbolPatchInfo::Entry::~Entry()
{
}
ElfSymbolPatchInfo::ElfSymbolPatchInfo()
: fSymbolName(),
fOriginalAddress(NULL),
fOriginalAddressImage(-1),
fEntries()
{
}
ElfSymbolPatchInfo::~ElfSymbolPatchInfo()
{
Unset();
}
status_t
ElfSymbolPatchInfo::InitCheck() const
{
return (fOriginalAddress && fSymbolName.Length() ? B_OK : B_NO_INIT);
}
const char*
ElfSymbolPatchInfo::GetSymbolName() const
{
return fSymbolName.String();
}
void*
ElfSymbolPatchInfo::GetOriginalAddress() const
{
return fOriginalAddress;
}
image_id
ElfSymbolPatchInfo::GetOriginalAddressImage() const
{
return fOriginalAddressImage;
}
status_t
ElfSymbolPatchInfo::Patch(void* newAddress)
{
status_t error = InitCheck();
if (error == B_OK) {
for (int i = 0; Entry* entry = EntryAt(i); i++)
entry->Patch(newAddress);
}
return error;
}
status_t
ElfSymbolPatchInfo::Restore()
{
return Patch(fOriginalAddress);
}
void
ElfSymbolPatchInfo::Unset()
{
for (int i = 0; Entry* entry = EntryAt(i); i++)
entry->Delete();
fEntries.MakeEmpty();
fSymbolName.SetTo("");
fOriginalAddress = NULL;
fOriginalAddressImage = -1;
}
status_t
ElfSymbolPatchInfo::SetSymbolName(const char* name)
{
fSymbolName.SetTo(name);
if (name && fSymbolName != name)
return B_NO_MEMORY;
return B_OK;
}
void
ElfSymbolPatchInfo::SetOriginalAddress(void* address, image_id image)
{
fOriginalAddress = address;
fOriginalAddressImage = image;
}
status_t
ElfSymbolPatchInfo::CreateEntry(image_id image, BList* targets)
{
if (!targets || targets->CountItems() == 0)
return B_BAD_VALUE;
Entry* entry = Entry::Create(image, (void***)targets->Items(),
targets->CountItems());
if (!entry)
return B_NO_MEMORY;
if (!fEntries.AddItem(entry)) {
entry->Delete();
return B_NO_MEMORY;
}
return B_OK;
}
bool
ElfSymbolPatchInfo::DeleteEntry(image_id image)
{
for (int i = 0; Entry* entry = EntryAt(i); i++) {
if (entry->GetImage() == image) {
fEntries.RemoveItem(i);
entry->Delete();
return true;
}
}
return false;
}
ElfSymbolPatchInfo::Entry*
ElfSymbolPatchInfo::EntryAt(int32 index)
{
return (Entry*)fEntries.ItemAt(index);
}
ElfSymbolPatchInfo::Entry*
ElfSymbolPatchInfo::EntryFor(image_id image)
{
for (int i = 0; Entry* entry = EntryAt(i); i++) {
if (entry->GetImage() == image)
return entry;
}
return NULL;
}
ElfSymbolPatcher::UpdateAdapter::UpdateAdapter()
{
}
ElfSymbolPatcher::UpdateAdapter::~UpdateAdapter()
{
}
void
ElfSymbolPatcher::UpdateAdapter::ImageAdded(ElfImage* image)
{
}
void
ElfSymbolPatcher::UpdateAdapter::ImageRemoved(ElfImage* image)
{
}
ElfSymbolPatcher::ElfSymbolPatcher()
: fImages(),
fInitStatus(B_NO_INIT)
{
fInitStatus = _Init();
if (fInitStatus != B_OK)
_Cleanup();
}
ElfSymbolPatcher::~ElfSymbolPatcher()
{
_Cleanup();
}
status_t
ElfSymbolPatcher::InitCheck() const
{
return fInitStatus;
}
status_t
ElfSymbolPatcher::Update(UpdateAdapter* updateAdapter)
{
if (InitCheck() != B_OK)
return B_NO_INIT;
int32 count = fImages.CountItems();
for (int i = count - 1; i >= 0; i--) {
ElfImage* image = _ImageAt(i);
image_info info;
if (get_image_info(image->GetID(), &info) != B_OK) {
if (updateAdapter)
updateAdapter->ImageRemoved(image);
fImages.RemoveItem(i);
delete image;
}
}
status_t error = B_OK;
image_info info;
int32 cookie = 0;
while (get_next_image_info(0, &cookie, &info) == B_OK) {
ElfImage* image = _ImageForID(info.id);
if (image)
continue;
image = new(std::nothrow) ElfImage;
if (!image)
return B_NO_MEMORY;
if (!fImages.AddItem(image)) {
delete image;
return B_NO_MEMORY;
}
error = image->SetTo(info.id);
if (updateAdapter)
updateAdapter->ImageAdded(image);
}
return error;
}
void
ElfSymbolPatcher::Unload()
{
for (int i = 0; ElfImage* image = _ImageAt(i); i++)
image->Unload();
}
status_t
ElfSymbolPatcher::GetSymbolPatchInfo(const char* symbolName,
ElfSymbolPatchInfo* info)
{
if (!symbolName || !info)
return B_BAD_VALUE;
if (InitCheck() != B_OK)
return B_NO_INIT;
info->Unset();
status_t error = info->SetSymbolName(symbolName);
if (error != B_OK)
return error;
for (int i = 0; ElfImage* image = _ImageAt(i); i++) {
BList patchTargets;
error = image->GetSymbolRelocations(symbolName, &patchTargets);
if (error != B_OK)
break;
if (patchTargets.CountItems() > 0) {
error = info->CreateEntry(image->GetID(), &patchTargets);
if (error != B_OK)
break;
}
void* address = NULL;
if (image->FindSymbol(symbolName, &address) == B_OK && address) {
if (info->GetOriginalAddress()) {
error = B_ERROR;
break;
} else
info->SetOriginalAddress(address, image->GetID());
}
}
if (!info->GetOriginalAddress())
{
error = B_ERROR;
}
if (error != B_OK)
info->Unset();
return error;
}
status_t
ElfSymbolPatcher::UpdateSymbolPatchInfo(ElfSymbolPatchInfo* info,
ElfImage* image)
{
if (!info || !image || !info->GetSymbolName())
return B_BAD_VALUE;
BList patchTargets;
status_t error
= image->GetSymbolRelocations(info->GetSymbolName(), &patchTargets);
if (error == B_OK)
error = info->CreateEntry(image->GetID(), &patchTargets);
return error;
}
status_t
ElfSymbolPatcher::_Init()
{
status_t error = B_OK;
image_info info;
int32 cookie = 0;
while (get_next_image_info(0, &cookie, &info) == B_OK) {
ElfImage* image = new(std::nothrow) ElfImage;
if (!image)
return B_NO_MEMORY;
if (!fImages.AddItem(image)) {
delete image;
return B_NO_MEMORY;
}
error = image->SetTo(info.id);
}
return error;
}
void
ElfSymbolPatcher::_Cleanup()
{
for (int i = 0; ElfImage* image = _ImageAt(i); i++)
delete image;
fImages.MakeEmpty();
}
ElfImage*
ElfSymbolPatcher::_ImageAt(int32 index) const
{
return (ElfImage*)fImages.ItemAt(index);
}
ElfImage*
ElfSymbolPatcher::_ImageForID(image_id id) const
{
for (int i = 0; ElfImage* image = _ImageAt(i); i++) {
if (image->GetID() == id)
return image;
}
return NULL;
}
ElfSymbolPatchGroup::ElfSymbolPatchGroup(ElfSymbolPatcher* patcher)
: fPatcher(patcher),
fPatchInfos(),
fOwnsPatcher(false),
fPatched(false)
{
if (!fPatcher) {
fPatcher = new(std::nothrow) ElfSymbolPatcher;
if (fPatcher) {
if (fPatcher->InitCheck() == B_OK)
fOwnsPatcher = true;
else {
delete fPatcher;
fPatcher = NULL;
}
}
}
}
ElfSymbolPatchGroup::~ElfSymbolPatchGroup()
{
RemoveAllPatches();
if (fPatcher && fOwnsPatcher)
delete fPatcher;
}
status_t
ElfSymbolPatchGroup::AddPatch(const char* symbolName, void* newAddress,
void** originalAddress)
{
if (!fPatcher)
return B_NO_INIT;
if (!symbolName || !originalAddress)
return B_BAD_VALUE;
PatchInfo* patchInfo = new(std::nothrow) PatchInfo;
if (!patchInfo)
return B_NO_MEMORY;
status_t error = fPatcher->GetSymbolPatchInfo(symbolName, patchInfo);
if (error == B_OK) {
if (fPatchInfos.AddItem(patchInfo)) {
patchInfo->fNewAddress = newAddress;
*originalAddress = patchInfo->GetOriginalAddress();
} else
error = B_NO_MEMORY;
}
if (error != B_OK && patchInfo) {
fPatchInfos.RemoveItem(patchInfo);
delete patchInfo;
}
return error;
}
void
ElfSymbolPatchGroup::RemoveAllPatches()
{
for (int i = 0; PatchInfo* info = (PatchInfo*)fPatchInfos.ItemAt(i); i++)
delete info;
fPatchInfos.MakeEmpty();
fPatched = false;
}
status_t
ElfSymbolPatchGroup::Patch()
{
if (!fPatcher)
return B_NO_INIT;
if (fPatched)
return B_OK;
for (int i = 0; PatchInfo* info = (PatchInfo*)fPatchInfos.ItemAt(i); i++)
info->Patch(info->fNewAddress);
fPatched = true;
return B_OK;
}
status_t
ElfSymbolPatchGroup::Restore()
{
if (!fPatcher)
return B_NO_INIT;
if (!fPatched)
return B_OK;
for (int i = 0; PatchInfo* info = (PatchInfo*)fPatchInfos.ItemAt(i); i++)
info->Restore();
fPatched = false;
return B_OK;
}
status_t
ElfSymbolPatchGroup::Update()
{
if (!fPatcher)
return B_NO_INIT;
return fPatcher->Update(this);
}
void
ElfSymbolPatchGroup::ImageAdded(ElfImage* image)
{
for (int i = 0; PatchInfo* info = (PatchInfo*)fPatchInfos.ItemAt(i); i++) {
fPatcher->UpdateSymbolPatchInfo(info, image);
if (fPatched) {
ElfSymbolPatchInfo::Entry* entry = info->EntryFor(image->GetID());
if (entry)
info->Patch(info->fNewAddress);
}
}
}
void
ElfSymbolPatchGroup::ImageRemoved(ElfImage* image)
{
int32 count = fPatchInfos.CountItems();
for (int i = count - 1; i >= 0; i--) {
PatchInfo* info = (PatchInfo*)fPatchInfos.ItemAt(i);
if (info->GetOriginalAddressImage() == image->GetID()) {
fPatchInfos.RemoveItem(i);
delete info;
} else
info->DeleteEntry(image->GetID());
}
}