/*
* Copyright 2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bcc/ExecutionEngine/ObjectLoader.h"
#include <utils/FileMap.h>
#include "bcc/ExecutionEngine/GDBJITRegistrar.h"
#include "bcc/Support/FileBase.h"
#include "bcc/Support/Log.h"
#include "ELFObjectLoaderImpl.h"
using namespace bcc;
ObjectLoader *ObjectLoader::Load(void *pMemStart, size_t pMemSize,
const char *pName,
SymbolResolverInterface &pResolver,
bool pEnableGDBDebug) {
ObjectLoader *result = NULL;
// Check parameters.
if ((pMemStart == NULL) || (pMemSize <= 0)) {
ALOGE("Invalid memory '%s' was given to load (memory addr: %p, size: %u)",
pName, pMemStart, static_cast<unsigned>(pMemSize));
goto bail;
}
// Create result object
result = new (std::nothrow) ObjectLoader();
if (result == NULL) {
ALOGE("Out of memory when create object loader for %s!", pName);
goto bail;
}
// Currently, only ELF object loader is supported. Therefore, there's no codes
// to detect the object file type and to select the one appropriated. Directly
// try out the ELF object loader.
result->mImpl = new (std::nothrow) ELFObjectLoaderImpl();
if (result->mImpl == NULL) {
ALOGE("Out of memory when create ELF object loader for %s", pName);
goto bail;
}
// Load the object file.
if (!result->mImpl->load(pMemStart, pMemSize)) {
ALOGE("Failed to load %s!", pName);
goto bail;
}
// Perform relocation.
if (!result->mImpl->relocate(pResolver)) {
ALOGE("Error occurred when performs relocation on %s!", pName);
goto bail;
}
// GDB debugging is enabled. Note that error occurrs during the setup of
// debugging won't failed the object load. Only a warning is issued to notify
// that the debugging is disabled due to the failure.
if (pEnableGDBDebug) {
// GDB's JIT debugging requires the source object file corresponded to the
// process image desired to debug with. And some fields in the object file
// must be updated to record the runtime information after it's loaded into
// memory. For example, GDB's JIT debugging requires an ELF file with the
// value of sh_addr in the section header to be the memory address that the
// section lives in the process image. Therefore, a writable memory with its
// contents initialized to the contents of pFile is created.
result->mDebugImage = new (std::nothrow) uint8_t [ pMemSize ];
if (result->mDebugImage != NULL) {
::memcpy(result->mDebugImage, pMemStart, pMemSize);
if (!result->mImpl->prepareDebugImage(result->mDebugImage, pMemSize)) {
ALOGW("GDB debug for %s is enabled by the user but won't work due to "
"failure debug image preparation!", pName);
} else {
registerObjectWithGDB(
reinterpret_cast<const ObjectBuffer *>(result->mDebugImage),
pMemSize);
}
}
}
return result;
bail:
delete result;
return NULL;
}
ObjectLoader *ObjectLoader::Load(FileBase &pFile,
SymbolResolverInterface &pResolver,
bool pEnableGDBDebug) {
size_t file_size;
android::FileMap *file_map = NULL;
const char *input_filename = pFile.getName().c_str();
ObjectLoader *result = NULL;
// Check the inputs.
if (pFile.hasError()) {
ALOGE("Input file %s to the object loader is in the invalid state! (%s)",
input_filename, pFile.getErrorMessage().c_str());
return NULL;
}
// Get the file size.
file_size = pFile.getSize();
if (pFile.hasError()) {
ALOGE("Failed to get size of file %s! (%s)", input_filename,
pFile.getErrorMessage().c_str());
return NULL;
}
// Abort on empty file.
if (file_size <= 0) {
ALOGE("Empty file %s to the object loader.", input_filename);
return NULL;
}
// Create memory map for the input file.
file_map = pFile.createMap(0, file_size, /* pIsReadOnly */true);
if ((file_map == NULL) || pFile.hasError()) {
ALOGE("Failed to map the file %s to the memory! (%s)", input_filename,
pFile.getErrorMessage().c_str());
return NULL;
}
// Delegate the load request.
result = Load(file_map->getDataPtr(), file_size, input_filename, pResolver,
pEnableGDBDebug);
// No whether the load is successful or not, file_map is no longer needed. On
// success, there's a copy of the object corresponded to the pFile in the
// memory. Therefore, file_map can be safely released.
file_map->release();
return result;
}
void *ObjectLoader::getSymbolAddress(const char *pName) const {
return mImpl->getSymbolAddress(pName);
}
size_t ObjectLoader::getSymbolSize(const char *pName) const {
return mImpl->getSymbolSize(pName);
}
bool ObjectLoader::getSymbolNameList(android::Vector<const char *>& pNameList,
SymbolType pType) const {
return mImpl->getSymbolNameList(pNameList, pType);
}
ObjectLoader::~ObjectLoader() {
delete mImpl;
delete [] reinterpret_cast<uint8_t *>(mDebugImage);
}