| /* |
| * 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); |
| } |