122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao//===- ObjectBuilder.cpp --------------------------------------------------===// 222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// 322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// The MCLinker Project 422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// 522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// This file is distributed under the University of Illinois Open Source 622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// License. See LICENSE.TXT for details. 722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// 822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao//===----------------------------------------------------------------------===// 937b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Object/ObjectBuilder.h" 1037b74a387bb3993387029859c2d9d051c41c724eStephen Hines 1137b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/IRBuilder.h" 1237b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/LinkerScript.h" 1337b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Module.h" 1437b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Fragment/AlignFragment.h" 1537b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Fragment/FillFragment.h" 1637b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Fragment/NullFragment.h" 1737b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/LD/DebugString.h" 1837b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/LD/EhFrame.h" 1937b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/LD/LDSection.h" 2037b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/LD/SectionData.h" 2137b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include "mcld/Object/SectionMap.h" 2222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 2322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao#include <llvm/Support/Casting.h> 2422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 2537b74a387bb3993387029859c2d9d051c41c724eStephen Hinesnamespace mcld { 2622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 2722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao//===----------------------------------------------------------------------===// 2822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao// ObjectBuilder 2922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao//===----------------------------------------------------------------------===// 3037b74a387bb3993387029859c2d9d051c41c724eStephen HinesObjectBuilder::ObjectBuilder(Module& pTheModule) : m_Module(pTheModule) { 3122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao} 3222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 3322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao/// CreateSection - create an output section. 3422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei LiaoLDSection* ObjectBuilder::CreateSection(const std::string& pName, 3522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao LDFileFormat::Kind pKind, 3622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao uint32_t pType, 3722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao uint32_t pFlag, 3837b74a387bb3993387029859c2d9d051c41c724eStephen Hines uint32_t pAlign) { 3922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // try to get one from output LDSection 4087f34658dec9097d987d254a990ea7f311bfc95fStephen Hines SectionMap::const_mapping pair = 4137b74a387bb3993387029859c2d9d051c41c724eStephen Hines m_Module.getScript().sectionMap().find("*", pName); 4287f34658dec9097d987d254a990ea7f311bfc95fStephen Hines 4387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines std::string output_name = (pair.first == NULL) ? pName : pair.first->name(); 4487f34658dec9097d987d254a990ea7f311bfc95fStephen Hines 4587f34658dec9097d987d254a990ea7f311bfc95fStephen Hines LDSection* output_sect = m_Module.getSection(output_name); 4637b74a387bb3993387029859c2d9d051c41c724eStephen Hines if (output_sect == NULL) { 4787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines output_sect = LDSection::Create(pName, pKind, pType, pFlag); 4887f34658dec9097d987d254a990ea7f311bfc95fStephen Hines output_sect->setAlign(pAlign); 4987f34658dec9097d987d254a990ea7f311bfc95fStephen Hines m_Module.getSectionTable().push_back(output_sect); 5087f34658dec9097d987d254a990ea7f311bfc95fStephen Hines } 5122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao return output_sect; 5222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao} 5322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 5422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao/// MergeSection - merge the pInput section to the pOutput section 5587f34658dec9097d987d254a990ea7f311bfc95fStephen HinesLDSection* ObjectBuilder::MergeSection(const Input& pInputFile, 5637b74a387bb3993387029859c2d9d051c41c724eStephen Hines LDSection& pInputSection) { 5737b74a387bb3993387029859c2d9d051c41c724eStephen Hines SectionMap::mapping pair = m_Module.getScript().sectionMap().find( 5837b74a387bb3993387029859c2d9d051c41c724eStephen Hines pInputFile.path().native(), pInputSection.name()); 5987f34658dec9097d987d254a990ea7f311bfc95fStephen Hines 6087f34658dec9097d987d254a990ea7f311bfc95fStephen Hines if (pair.first != NULL && pair.first->isDiscard()) { 6187f34658dec9097d987d254a990ea7f311bfc95fStephen Hines pInputSection.setKind(LDFileFormat::Ignore); 6287f34658dec9097d987d254a990ea7f311bfc95fStephen Hines return NULL; 6387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines } 6487f34658dec9097d987d254a990ea7f311bfc95fStephen Hines 6537b74a387bb3993387029859c2d9d051c41c724eStephen Hines std::string output_name = 6637b74a387bb3993387029859c2d9d051c41c724eStephen Hines (pair.first == NULL) ? pInputSection.name() : pair.first->name(); 6722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao LDSection* target = m_Module.getSection(output_name); 6822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 6937b74a387bb3993387029859c2d9d051c41c724eStephen Hines if (target == NULL) { 7022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao target = LDSection::Create(output_name, 7122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pInputSection.kind(), 7222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pInputSection.type(), 7322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pInputSection.flag()); 7422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao target->setAlign(pInputSection.align()); 7522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao m_Module.getSectionTable().push_back(target); 7622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 7722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 7822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao switch (target->kind()) { 7922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao case LDFileFormat::EhFrame: { 8022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao EhFrame* eh_frame = NULL; 8122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao if (target->hasEhFrame()) 8222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao eh_frame = target->getEhFrame(); 8322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao else 8422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao eh_frame = IRBuilder::CreateEhFrame(*target); 8522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 8687f34658dec9097d987d254a990ea7f311bfc95fStephen Hines eh_frame->merge(pInputFile, *pInputSection.getEhFrame()); 8787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines UpdateSectionAlign(*target, pInputSection); 886f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines return target; 8922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 9037b74a387bb3993387029859c2d9d051c41c724eStephen Hines case LDFileFormat::DebugString: { 9137b74a387bb3993387029859c2d9d051c41c724eStephen Hines DebugString* debug_str = NULL; 9237b74a387bb3993387029859c2d9d051c41c724eStephen Hines if (target->hasDebugString()) 9337b74a387bb3993387029859c2d9d051c41c724eStephen Hines debug_str = target->getDebugString(); 9437b74a387bb3993387029859c2d9d051c41c724eStephen Hines else 9537b74a387bb3993387029859c2d9d051c41c724eStephen Hines debug_str = IRBuilder::CreateDebugString(*target); 9637b74a387bb3993387029859c2d9d051c41c724eStephen Hines 9737b74a387bb3993387029859c2d9d051c41c724eStephen Hines debug_str->merge(pInputSection); 9837b74a387bb3993387029859c2d9d051c41c724eStephen Hines UpdateSectionAlign(*target, pInputSection); 9937b74a387bb3993387029859c2d9d051c41c724eStephen Hines return target; 10037b74a387bb3993387029859c2d9d051c41c724eStephen Hines } 10122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao default: { 10287f34658dec9097d987d254a990ea7f311bfc95fStephen Hines if (!target->hasSectionData()) 10387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines IRBuilder::CreateSectionData(*target); 10487f34658dec9097d987d254a990ea7f311bfc95fStephen Hines 10522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao SectionData* data = NULL; 10687f34658dec9097d987d254a990ea7f311bfc95fStephen Hines if (pair.first != NULL) { 10787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines assert(pair.second != NULL); 10887f34658dec9097d987d254a990ea7f311bfc95fStephen Hines data = pair.second->getSection()->getSectionData(); 10937b74a387bb3993387029859c2d9d051c41c724eStephen Hines 11037b74a387bb3993387029859c2d9d051c41c724eStephen Hines // force input alignment from ldscript if any 11137b74a387bb3993387029859c2d9d051c41c724eStephen Hines if (pair.first->prolog().hasSubAlign()) { 11237b74a387bb3993387029859c2d9d051c41c724eStephen Hines pInputSection.setAlign(pair.second->getSection()->align()); 11337b74a387bb3993387029859c2d9d051c41c724eStephen Hines } 11487f34658dec9097d987d254a990ea7f311bfc95fStephen Hines } else { 11587f34658dec9097d987d254a990ea7f311bfc95fStephen Hines // orphan section 11622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao data = target->getSectionData(); 11787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines } 11822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 1196f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines if (MoveSectionData(*pInputSection.getSectionData(), *data)) { 1206f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines UpdateSectionAlign(*target, pInputSection); 1216f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines return target; 1226f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines } 1236f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines return NULL; 12422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 12522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 1266f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines return target; 12722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao} 12822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 12922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao/// MoveSectionData - move the fragments of pTO section data to pTo 13037b74a387bb3993387029859c2d9d051c41c724eStephen Hinesbool ObjectBuilder::MoveSectionData(SectionData& pFrom, SectionData& pTo) { 13122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao assert(&pFrom != &pTo && "Cannot move section data to itself!"); 13222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 13387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines uint64_t offset = pTo.getSection().size(); 13422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao AlignFragment* align = NULL; 13522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao if (pFrom.getSection().align() > 1) { 13622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // if the align constraint is larger than 1, append an alignment 13737b74a387bb3993387029859c2d9d051c41c724eStephen Hines unsigned int alignment = pFrom.getSection().align(); 13837b74a387bb3993387029859c2d9d051c41c724eStephen Hines align = new AlignFragment(/*alignment*/alignment, 13937b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*the filled value*/0x0, 14037b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*the size of filled value*/1u, 14137b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*max bytes to emit*/alignment - 1); 14222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao align->setOffset(offset); 14322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao align->setParent(&pTo); 14422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pTo.getFragmentList().push_back(align); 14522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao offset += align->size(); 14622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 14722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 14822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // move fragments from pFrom to pTO 14922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao SectionData::FragmentListType& from_list = pFrom.getFragmentList(); 15022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao SectionData::FragmentListType& to_list = pTo.getFragmentList(); 15122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao SectionData::FragmentListType::iterator frag, fragEnd = from_list.end(); 15222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao for (frag = from_list.begin(); frag != fragEnd; ++frag) { 15322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao frag->setParent(&pTo); 15422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao frag->setOffset(offset); 15522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao offset += frag->size(); 15622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 15722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao to_list.splice(to_list.end(), from_list); 15822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 15922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // set up pTo's header 16022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pTo.getSection().setSize(offset); 16122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 16222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao return true; 16322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao} 16422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 165551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines/// UpdateSectionAlign - update alignment for input section 16637b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid ObjectBuilder::UpdateSectionAlign(LDSection& pTo, const LDSection& pFrom) { 1676f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines if (pFrom.align() > pTo.align()) 1686f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines pTo.setAlign(pFrom.align()); 1696f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines} 1706f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines 171551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines/// UpdateSectionAlign - update alignment for input section 172551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hinesvoid ObjectBuilder::UpdateSectionAlign(LDSection& pSection, 17337b74a387bb3993387029859c2d9d051c41c724eStephen Hines uint32_t pAlignConstraint) { 174551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines if (pSection.align() < pAlignConstraint) 175551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines pSection.setAlign(pAlignConstraint); 176551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines} 177551ae4ebd3e9d137ea668fb83ae4a55b8cfba451Stephen Hines 17822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao/// AppendFragment - To append pFrag to the given SectionData pSD. 17922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liaouint64_t ObjectBuilder::AppendFragment(Fragment& pFrag, 18022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao SectionData& pSD, 18137b74a387bb3993387029859c2d9d051c41c724eStephen Hines uint32_t pAlignConstraint) { 18222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // get initial offset. 18387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines uint64_t offset = 0; 18422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao if (!pSD.empty()) 18522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao offset = pSD.back().getOffset() + pSD.back().size(); 18622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 18722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao AlignFragment* align = NULL; 18822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao if (pAlignConstraint > 1) { 18922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // if the align constraint is larger than 1, append an alignment 19037b74a387bb3993387029859c2d9d051c41c724eStephen Hines align = new AlignFragment(/*alignment*/pAlignConstraint, 19137b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*the filled value*/0x0, 19237b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*the size of filled value*/1u, 19337b74a387bb3993387029859c2d9d051c41c724eStephen Hines /*max bytes to emit*/pAlignConstraint - 1); 19422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao align->setOffset(offset); 19522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao align->setParent(&pSD); 19622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pSD.getFragmentList().push_back(align); 19722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao offset += align->size(); 19822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao } 19922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 20022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // append the fragment 20122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pFrag.setParent(&pSD); 20222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pFrag.setOffset(offset); 20322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao pSD.getFragmentList().push_back(&pFrag); 20422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 20522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao // append the null fragment 20622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao offset += pFrag.size(); 20722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao NullFragment* null = new NullFragment(&pSD); 20822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao null->setOffset(offset); 20922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 21037b74a387bb3993387029859c2d9d051c41c724eStephen Hines if (align != NULL) 21122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao return align->size() + pFrag.size(); 21222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao else 21322add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao return pFrag.size(); 21422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao} 21522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao 21637b74a387bb3993387029859c2d9d051c41c724eStephen Hines} // namespace mcld 217