//
// ELF object format
//
//  Copyright (C) 2003-2007  Michael Urman
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Notes
//
// elf-objfmt uses the "linking" view of an ELF file:
// ELF header, an optional program header table, several sections,
// and a section header table
//
// The ELF header tells us some overall program information,
//   where to find the PHT (if it exists) with phnum and phentsize,
//   and where to find the SHT with shnum and shentsize
//
// The PHT doesn't seem to be generated by NASM for elftest.asm
//
// The SHT
//
// Each Section is spatially disjoint, and has exactly one SHT entry.
//
#include "ElfObject.h"

#include "llvm/ADT/SmallString.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "yasmx/Basic/Diagnostic.h"
#include "yasmx/Basic/SourceManager.h"
#include "yasmx/Parse/Directive.h"
#include "yasmx/Parse/DirHelpers.h"
#include "yasmx/Parse/NameValue.h"
#include "yasmx/Support/bitcount.h"
#include "yasmx/Support/registry.h"
#include "yasmx/Support/scoped_array.h"
#include "yasmx/Arch.h"
#include "yasmx/BytecodeContainer.h"
#include "yasmx/BytecodeOutput.h"
#include "yasmx/Bytecode.h"
#include "yasmx/Bytes_util.h"
#include "yasmx/DebugFormat.h"
#include "yasmx/Expr_util.h"
#include "yasmx/Location_util.h"
#include "yasmx/Object.h"
#include "yasmx/Object_util.h"
#include "yasmx/Section.h"
#include "yasmx/StringTable.h"
#include "yasmx/Symbol_util.h"

#include "ElfMachine.h"
#include "ElfReloc.h"
#include "ElfSection.h"
#include "ElfSymbol.h"
#include "ElfTypes.h"


using namespace yasm;
using namespace yasm::objfmt;

static inline bool
isLocal(const Symbol& sym)
{
    int vis = sym.getVisibility();
    return (vis == Symbol::LOCAL || (vis & Symbol::DLOCAL) != 0);
}

static inline bool
byIndex(const Symbol& s1, const Symbol& s2)
{
    const ElfSymbol* e1 = s1.getAssocData<ElfSymbol>();
    const ElfSymbol* e2 = s2.getAssocData<ElfSymbol>();
    if (e1 && !e2)
        return true;
    if (!e1 && e2)
        return false;
    if (!e1 && !e2)
        return false;   // doesn't matter
    return e1->getSymbolIndex() < e2->getSymbolIndex();
}

ElfGroup::ElfGroup()
    : flags(0)
{
}

ElfGroup::~ElfGroup()
{
}

ElfObject::ElfObject(const ObjectFormatModule& module,
                     Object& object,
                     unsigned int bits)
    : ObjectFormat(module, object)
    , m_machine(0)
    , m_file_elfsym(0)
    , m_dotdotsym(0)
    , m_symvers_owner(m_symvers)
    , m_groups_owner(m_groups)
{
    if (bits == 32)
        m_config.cls = ELFCLASS32;
    else if (bits == 64)
        m_config.cls = ELFCLASS64;
    else
    {
        assert(bits == 0 && "unknown ELF bits setting");
        m_config.cls = ELFCLASS32;
    }

    m_machine.reset(CreateElfMachine(*m_object.getArch(),
                                     m_config.cls).release());
    m_machine->Configure(&m_config);
}

ElfObject::~ElfObject()
{
}

Elf32Object::~Elf32Object()
{
}

bool
Elf32Object::isOkObject(Object& object)
{
    return isOkElfMachine(*object.getArch(), ELFCLASS32);
}

Elf64Object::~Elf64Object()
{
}

bool
Elf64Object::isOkObject(Object& object)
{
    return isOkElfMachine(*object.getArch(), ELFCLASS64);
}

static bool
TasteCommon(const llvm::MemoryBuffer& in,
            /*@out@*/ std::string* arch_keyword,
            /*@out@*/ std::string* machine,
            ElfClass cls)
{
    ElfConfig config;

    // Read header
    if (!config.ReadProgramHeader(in))
        return false;

    // Check class
    if (config.cls != cls)
        return false;

    // for now, just handle this here
    switch (config.machine_type)
    {
        case EM_386:
            arch_keyword->assign("x86");
            machine->assign("x86");
            break;
        case EM_X86_64:
            arch_keyword->assign("x86");
            machine->assign("amd64");
            break;
        default:
            return false;
    }
    return true;
}

bool
Elf32Object::Taste(const llvm::MemoryBuffer& in,
                   /*@out@*/ std::string* arch_keyword,
                   /*@out@*/ std::string* machine)
{
    return TasteCommon(in, arch_keyword, machine, ELFCLASS32);
}

bool
Elf64Object::Taste(const llvm::MemoryBuffer& in,
                   /*@out@*/ std::string* arch_keyword,
                   /*@out@*/ std::string* machine)
{
    return TasteCommon(in, arch_keyword, machine, ELFCLASS64);
}

static inline bool
LoadStringTable(StringTable* strtab,
                const llvm::MemoryBuffer& in,
                const ElfSection& elfsect,
                Diagnostic& diags)
{
    const char* start = in.getBufferStart() + elfsect.getFileOffset();
    const char* end = start + elfsect.getSize().getUInt();
    if (end > in.getBufferEnd())
    {
        diags.Report(SourceLocation(), diag::err_string_table_unreadable);
        return false;
    }
    strtab->Read(reinterpret_cast<const unsigned char*>(start), end-start);
    return true;
}

bool
ElfObject::Read(SourceManager& sm, Diagnostic& diags)
{
    const llvm::MemoryBuffer& in = *sm.getBuffer(sm.getMainFileID());

    // Read header
    if (!m_config.ReadProgramHeader(in))
    {
        diags.Report(SourceLocation(), diag::err_not_file_type) << "ELF";
        return false;
    }

    // Can't handle files without section table yet
    if (m_config.secthead_pos == 0)
    {
        diags.Report(SourceLocation(), diag::err_no_section_table);
        return false;
    }

    // Read section string table (needed for section names)
    std::auto_ptr<ElfSection>
        shstrtab_sect(new ElfSection(m_config, in, m_config.shstrtab_index,
                                     diags));
    if (diags.hasErrorOccurred())
        return false;
    StringTable shstrtab;
    if (!LoadStringTable(&shstrtab, in, *shstrtab_sect, diags))
        return false;

    // Read all section headers

    // owned storage for "misc" sections (e.g. relocation sections)
    stdx::ptr_vector<ElfSection> misc_sections;
    stdx::ptr_vector_owner<ElfSection> misc_sections_owner(misc_sections);
    misc_sections.reserve(m_config.secthead_count);

    // indexed array of all ElfSections by section index
    util::scoped_array<ElfSection*>
        elfsects(new ElfSection*[m_config.secthead_count]);
    // indexed array of all Sections by section index
    util::scoped_array<Section*>
        sections(new Section*[m_config.secthead_count]);

    // special sections
    ElfSection* strtab_sect = 0;
    ElfSection* symtab_sect = 0;

    // read section headers
    for (unsigned int i=0; i<m_config.secthead_count; ++i)
    {
        // read section header and save by index
        std::auto_ptr<ElfSection> elfsect(new ElfSection(m_config, in, i,
                                                         diags));
        if (diags.hasErrorOccurred())
            return false;
        elfsects[i] = elfsect.get();

        llvm::StringRef sectname = shstrtab.getString(elfsect->getName());
        if (sectname == ".strtab")
        {
            strtab_sect = elfsect.get();
        }
        else if (sectname == ".symtab")
        {
            symtab_sect = elfsect.get();
        }

        ElfSectionType secttype = elfsect->getType();
        if (secttype == SHT_NULL ||
            secttype == SHT_SYMTAB ||
            secttype == SHT_STRTAB ||
            secttype == SHT_RELA ||
            secttype == SHT_REL)
        {
            misc_sections.push_back(elfsect.release());
            sections[i] = 0;

            // try to pick these up by section type if not set
            if (secttype == SHT_SYMTAB && symtab_sect == 0)
                symtab_sect = elfsect.get();
            else if (secttype == SHT_STRTAB && strtab_sect == 0)
                strtab_sect = elfsect.get();

            // if any section is RELA, set config to RELA
            if (secttype == SHT_RELA)
                m_config.rela = true;
        }
        else
        {
            std::auto_ptr<Section> section = elfsect->CreateSection(shstrtab);
            if (!elfsect->LoadSectionData(*section, in, diags))
                return false;
            sections[i] = section.get();

            // Associate section data with section
            section->AddAssocData(elfsect);

            // Add section to object
            m_object.AppendSection(section);
        }
    }

    // Symbol table by index (needed for relocation lookups by index)
    std::vector<SymbolRef> symtab;

    // read symtab string table and symbol table (if present)
    if (symtab_sect != 0)
    {
        // get string table section index from symtab link field if reasonable
        ElfSectionIndex link = symtab_sect->getLink();
        if (link < m_config.secthead_count &&
            elfsects[link]->getType() == SHT_STRTAB)
            strtab_sect = elfsects[link];

        if (strtab_sect == 0)
        {
            diags.Report(SourceLocation(), diag::err_no_symbol_string_table);
            return false;
        }

        // load symbol string table
        StringTable strtab;
        if (!LoadStringTable(&strtab, in, *strtab_sect, diags))
            return false;

        // load symbol table
        if (!m_config.ReadSymbolTable(in, *symtab_sect, symtab, m_object,
                                      strtab, &sections[0], diags))
            return false;
    }

    // go through misc sections to load relocations
    for (unsigned int i=0; i<m_config.secthead_count; ++i)
    {
        ElfSection* reloc_sect = elfsects[i];
        ElfSectionType secttype = reloc_sect->getType();
        if (secttype != SHT_REL && secttype != SHT_RELA)
            continue;

        // get symbol table section index from link field (if valid)
        ElfSection* rel_symtab_sect = symtab_sect;
        ElfSectionIndex link = reloc_sect->getLink();
        if (link < m_config.secthead_count &&
            elfsects[link]->getType() == SHT_SYMTAB)
        {
            if (rel_symtab_sect != elfsects[link])
            {
                diags.Report(SourceLocation(),
                             diag::err_multiple_symbol_tables);
                return false;
            }
        }

        // section relocs apply to is indicated by info field
        ElfSectionIndex info = reloc_sect->getInfo();
        if (link >= m_config.secthead_count || sections[info] == 0)
            continue;

        // load relocations
        elfsects[info]->ReadRelocs(in, *reloc_sect, *sections[info],
                                   *m_machine, symtab, secttype == SHT_RELA);
    }
    return true;
}

void
ElfObject::InitSymbols(llvm::StringRef parser)
{
    // Set object options
    m_object.getOptions().DisableGlobalSubRelative = true;

    // Add .file symbol
    SymbolRef filesym = m_object.AppendSymbol(".file");
    filesym->DefineSpecial(Symbol::LOCAL);

    std::auto_ptr<ElfSymbol> elfsym(new ElfSymbol());
    elfsym->setSectionIndex(SHN_ABS);
    elfsym->setBinding(STB_LOCAL);
    elfsym->setType(STT_FILE);
    elfsym->setSymbolIndex(1);  // by convention
    m_file_elfsym = elfsym.get();

    filesym->AddAssocData(elfsym);

    // Create ..sym special symbol (NASM only)
    if (parser.equals_lower("nasm"))
    {
        m_dotdotsym = m_object.AddSpecialSymbol("sym");
        m_dotdotsym->DefineSpecial(Symbol::EXTERN);
    }

    // Create machine-specific special symbols
    m_machine->AddSpecialSymbols(m_object, parser);
}

ElfSymbol&
ElfObject::BuildSymbol(Symbol& sym)
{
    ElfSymbol* elfsym = sym.getAssocData<ElfSymbol>();

    if (!elfsym)
    {
        elfsym = new ElfSymbol;
        sym.AddAssocData(std::auto_ptr<ElfSymbol>(elfsym));
    }

    return *elfsym;
}

void
ElfObject::BuildExtern(Symbol& sym, Diagnostic& diags)
{
    const NameValues* objext_nvs = getObjextNameValues(sym);

    if (objext_nvs)
    {
        for (NameValues::const_iterator nv=objext_nvs->begin(),
             end=objext_nvs->end(); nv != end; ++nv)
        {
            if (nv->isString())
            {
                diags.Report(nv->getValueRange().getBegin(),
                             diag::warn_unrecognized_symbol_type)
                    << nv->getString();
                return;
            }
        }
    }

    ElfSymbol& elfsym = BuildSymbol(sym);
    if (elfsym.getBinding() == STB_LOCAL)
        elfsym.setBinding(STB_GLOBAL);
}

static bool
GlobalNameValueFallback(NameValue& nv,
                        SourceLocation dir_source,
                        Diagnostic& diags,
                        Object* object,
                        Expr::Ptr* size)

{
    if (!nv.isExpr() && nv.isId())
    {
        diags.Report(nv.getValueRange().getBegin(),
                     diag::warn_unrecognized_symbol_type)
            << nv.getId();
        return true;
    }
    else if (nv.isExpr() && size->get() == 0)
    {
        *size = nv.ReleaseExpr(*object);
        return true;
    }
    else
        return DirNameValueWarn(nv, dir_source, diags);
}

static inline void
GlobalSetVis(NameValue& nv,
             Diagnostic& diags,
             ElfSymbolVis* vis_out,
             unsigned int* vis_count,
             SourceLocation* vis_source,
             ElfSymbolVis vis)
{
    *vis_out = vis;
    *vis_count = *vis_count + 1;
    *vis_source = nv.getValueRange().getBegin();
}


void
ElfObject::BuildGlobal(Symbol& sym, Diagnostic& diags)
{
    Expr::Ptr size(0);
    unsigned long type = STT_NOTYPE;    // ElfSymbolType
    unsigned int nvis = 0;
    SourceLocation vis_source;

    ElfSymbol& elfsym = BuildSymbol(sym);
    ElfSymbolVis vis = elfsym.getVisibility();

    DirHelpers helpers;

    helpers.Add("function", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, STT_FUNC));
    helpers.Add("data", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, STT_OBJECT));
    helpers.Add("object", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, STT_OBJECT));
    helpers.Add("object", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, STT_OBJECT));
    helpers.Add("internal", false,
                TR1::bind(&GlobalSetVis, _1, _2, &vis, &nvis, &vis_source,
                          STV_INTERNAL));
    helpers.Add("hidden", false,
                TR1::bind(&GlobalSetVis, _1, _2, &vis, &nvis, &vis_source,
                          STV_HIDDEN));
    helpers.Add("protected", false,
                TR1::bind(&GlobalSetVis, _1, _2, &vis, &nvis, &vis_source,
                          STV_PROTECTED));

    NameValues* objext_nvs = getObjextNameValues(sym);
    if (objext_nvs)
    {
        helpers(objext_nvs->begin(), objext_nvs->end(), sym.getDeclSource(),
                diags, TR1::bind(&GlobalNameValueFallback, _1, _2, _3,
                                 &m_object, &size));
    }

    if (nvis > 1)
        diags.Report(vis_source, diag::warn_multiple_symbol_visibility);

    if (elfsym.getBinding() == STB_LOCAL)
        elfsym.setBinding(STB_GLOBAL);
    if (!elfsym.hasType())
        elfsym.setType(static_cast<ElfSymbolType>(type));
    elfsym.setVisibility(vis);
    if (size.get() != 0 && !elfsym.hasSize())
        elfsym.setSize(*size, sym.getDeclSource());
}

void
ElfObject::BuildCommon(Symbol& sym, Diagnostic& diags)
{
    NameValues* objext_nvs = getObjextNameValues(sym);
    bool has_align = false;
    unsigned long addralign = 0;

    if (objext_nvs)
    {
        for (NameValues::iterator nv=objext_nvs->begin(),
             end=objext_nvs->end(); nv != end; ++nv)
        {
            if (!nv->getName().empty())
            {
                diags.Report(nv->getNameSource(),
                             diag::warn_unrecognized_qualifier);
                continue;
            }

            if (!nv->isExpr())
            {
                diags.Report(nv->getValueRange().getBegin(),
                             diag::err_align_not_integer);
                return;
            }

            std::auto_ptr<Expr> align_expr = nv->ReleaseExpr(m_object);
            if (!align_expr->isIntNum())
            {
                diags.Report(nv->getValueRange().getBegin(),
                             diag::err_align_not_integer);
                return;
            }
            addralign = align_expr->getIntNum().getUInt();
            has_align = true;

            // Alignments must be a power of two.
            if (!isExp2(addralign))
            {
                diags.Report(nv->getValueRange().getBegin(),
                             diag::err_value_power2);
                return;
            }
        }
    }

    ElfSymbol& elfsym = BuildSymbol(sym);
    elfsym.setSectionIndex(SHN_COMMON);
    if (elfsym.getBinding() == STB_LOCAL)
        elfsym.setBinding(STB_GLOBAL);
    if (!elfsym.hasSize())
        elfsym.setSize(*getCommonSize(sym), sym.getDeclSource());
    if (!elfsym.hasType())
        elfsym.setType(STT_OBJECT);
    if (!has_align)
    {
        Expr size = elfsym.getSize();
        if (!size.isEmpty())
        {
            if (!ExpandEqu(size))
            {
                diags.Report(elfsym.getSizeSource(),
                             diag::err_equ_circular_reference);
                return;
            }
            SimplifyCalcDist(size, diags);
            if (size.isIntNum())
            {
                unsigned long sz = size.getIntNum().getUInt();
                // set alignment to largest power of two <= size, up to 16.
                if (sz < 2)
                    addralign = 1;
                else if (sz < 4)
                    addralign = 2;
                else if (sz < 8)
                    addralign = 4;
                else if (sz < 16)
                    addralign = 8;
                else
                    addralign = 16;
            }
            else
                diags.Report(elfsym.getSizeSource(), diag::err_size_integer);
        }

    }
    elfsym.setValue(addralign);
}

void
ElfObject::setSymbolSectionValue(Symbol& sym, ElfSymbol& elfsym)
{
    Location loc;
    if (!sym.getLabel(&loc))
        return;

    if (loc.bc)
    {
        elfsym.setSection(loc.bc->getContainer()->getSection());
        elfsym.setValue(loc.getOffset());
    }
}

void
ElfObject::FinalizeSymbol(Symbol& sym,
                          StringTable& strtab,
                          bool local_names,
                          Diagnostic& diags)
{
    int vis = sym.getVisibility();
    ElfSymbol* elfsym = sym.getAssocData<ElfSymbol>();

    if (vis & Symbol::EXTERN)
    {
        BuildExtern(sym, diags);
        elfsym = sym.getAssocData<ElfSymbol>();
    }
    else if (vis & Symbol::COMMON)
    {
        BuildCommon(sym, diags);
        elfsym = sym.getAssocData<ElfSymbol>();
    }

    if (sym.isDefined())
    {
        if ((vis & Symbol::COMMON) == 0 && (vis & Symbol::GLOBAL) != 0)
        {
            BuildGlobal(sym, diags);
            elfsym = sym.getAssocData<ElfSymbol>();
        }

        if (!elfsym)
        {
            Location loc = {0, 0};
            if (!sym.getLabel(&loc))
            {
                if (!sym.getEqu() && !sym.isAbsoluteSymbol())
                    return;
            }

            Section* sect = 0;
            if (loc.bc)
                sect = loc.bc->getContainer()->getSection();

            // Locals (except when debugging) do not need to be
            // in the symbol table, unless they're a section.
            bool is_sect = false;
            if (sect)
            {
                ElfSection* elfsect = sect->getAssocData<ElfSection>();
                if (elfsect && sect->getSymbol() == &sym)
                    is_sect = true;
            }

            if (!is_sect)
            {
                if (!local_names)
                    return;
                // GCC names its internal symbols .Lxxxx; follow gas' lead and
                // don't output these symbols even if local_names is enabled.
                llvm::StringRef name = sym.getName();
                if (name.size() > 2 && name[0] == '.' && name[1] == 'L')
                    return;
                // Don't output GAS parser local labels.
                if (name.size() > 1 && name[0] == 'L' &&
                    name.rfind('\001') != llvm::StringRef::npos)
                    return;
            }

            elfsym = &BuildSymbol(sym);
            if (is_sect)
                elfsym->setType(STT_SECTION);
        }

        setSymbolSectionValue(sym, *elfsym);
    }

    if (!elfsym)
        return;

    elfsym->Finalize(sym, diags);

    if (elfsym->isInTable() && !elfsym->hasName() &&
        elfsym->getType() != STT_SECTION)
        elfsym->setName(strtab.getIndex(sym.getName()));
}

namespace {
class ElfOutput : public BytecodeStreamOutput
{
public:
    ElfOutput(llvm::raw_fd_ostream& os,
              ElfObject& objfmt,
              Object& object,
              Diagnostic& diags);
    ~ElfOutput();

    void OutputGroup(ElfGroup& group);
    void OutputSection(Section& sect, StringTable& shstrtab);

    // OutputBytecode overrides
    bool ConvertValueToBytes(Value& value,
                             Location loc,
                             NumericOutput& num_out);
    bool ConvertSymbolToBytes(SymbolRef sym,
                              Location loc,
                              NumericOutput& num_out);

private:
    ElfObject& m_objfmt;
    Object& m_object;
    llvm::raw_fd_ostream& m_fd_os;
    BytecodeNoOutput m_no_output;
    SymbolRef m_GOT_sym;
};
} // anonymous namespace

ElfOutput::ElfOutput(llvm::raw_fd_ostream& os,
                     ElfObject& objfmt,
                     Object& object,
                     Diagnostic& diags)
    : BytecodeStreamOutput(os, diags)
    , m_objfmt(objfmt)
    , m_object(object)
    , m_fd_os(os)
    , m_no_output(diags)
    , m_GOT_sym(object.FindSymbol("_GLOBAL_OFFSET_TABLE_"))
{
}

ElfOutput::~ElfOutput()
{
}

bool
ElfOutput::ConvertSymbolToBytes(SymbolRef sym,
                                Location loc,
                                NumericOutput& num_out)
{
    std::auto_ptr<ElfReloc> reloc =
        m_objfmt.m_machine->MakeReloc(sym, loc.getOffset());
    if (reloc->setRel(false, m_GOT_sym, num_out.getSize(), false))
    {
        // allocate .rel[a] sections on a need-basis
        Section* sect = loc.bc->getContainer()->getSection();
        sect->AddReloc(std::auto_ptr<Reloc>(reloc.release()));
    }
    else
    {
        Diag(num_out.getSource(), diag::err_reloc_invalid_size);
    }

    m_object.getArch()->setEndian(num_out.getBytes());
    num_out.OutputInteger(0);
    return true;
}

bool
ElfOutput::ConvertValueToBytes(Value& value,
                               Location loc,
                               NumericOutput& num_out)
{
    m_object.getArch()->setEndian(num_out.getBytes());

    IntNum intn(0);
    if (value.OutputBasic(num_out, &intn, getDiagnostics()))
        return true;

    if (value.isRelative())
    {
        // We can't handle these types of values
        if (value.isSegOf() || value.isSectionRelative() ||
            value.getRShift() > 0 || value.getShift() > 0)
        {
            Diag(value.getSource().getBegin(), diag::err_reloc_too_complex);
            return false;
        }

        SymbolRef sym = value.getRelative();
        SymbolRef wrt = value.getWRT();

        if (wrt && wrt == m_objfmt.m_dotdotsym)
            wrt = SymbolRef(0);
        else if (wrt && isWRTElfSymRelative(*wrt))
            ;
        else if (wrt && isWRTElfPosAdjusted(*wrt))
            intn += loc.getOffset();
        else if (isLocal(*sym))
        {
            // Local symbols need relocation to their section's start, and
            // add in the offset of the bytecode (within the target section)
            // into the abs portion.
            //
            // This is only done if the symbol is relocated against the
            // section instead of the symbol itself.
            Location symloc;
            if (sym->getLabel(&symloc))
            {
                // Relocate to section start
                Section* sym_sect = symloc.bc->getContainer()->getSection();
                sym = sym_sect->getSymbol();

                intn += symloc.getOffset();
            }
        }

        bool pc_rel = false;
        IntNum intn2;
        if (value.CalcPCRelSub(&intn2, loc))
        {
            // Create PC-relative relocation type and fix up absolute portion.
            pc_rel = true;
            intn += intn2;
        }
        else if (value.hasSubRelative())
        {
            Diag(value.getSource().getBegin(), diag::err_reloc_too_complex);
            return false;
        }

        // Create relocation
        Section* sect = loc.bc->getContainer()->getSection();
        std::auto_ptr<ElfReloc> reloc =
            m_objfmt.m_machine->MakeReloc(sym, loc.getOffset());
        if (wrt)
        {
            if (!reloc->setWrt(wrt, value.getSize()))
            {
                Diag(value.getSource().getBegin(), diag::err_invalid_wrt);
            }
        }
        else
        {
            if (!reloc->setRel(pc_rel, m_GOT_sym, value.getSize(),
                               value.isSigned()))
            {
                Diag(value.getSource().getBegin(),
                     diag::err_reloc_invalid_size);
            }
        }

        if (reloc->isValid())
        {
            reloc->HandleAddend(&intn, m_objfmt.m_config, value.getInsnStart());
            sect->AddReloc(std::auto_ptr<Reloc>(reloc.release()));
        }
    }

    num_out.OutputInteger(intn);
    return true;
}

void
ElfOutput::OutputGroup(ElfGroup& group)
{
    uint64_t pos = m_os.tell();
    if (m_os.has_error())
    {
        Diag(SourceLocation(), diag::err_file_output_position);
        return;
    }

    m_fd_os.seek(group.elfsect->setFileOffset(pos));
    if (m_os.has_error())
    {
        Diag(SourceLocation(), diag::err_file_output_seek);
        return;
    }

    Bytes& scratch = getScratch();
    m_objfmt.m_config.setEndian(scratch);

    // sort and uniquify sections before output
    std::sort(group.sects.begin(), group.sects.end());
    std::vector<Section*>::iterator it =
        std::unique(group.sects.begin(), group.sects.end());
    group.sects.resize(it - group.sects.begin());

    Write32(scratch, group.flags);
    for (std::vector<Section*>::const_iterator i=group.sects.begin(),
         end=group.sects.end(); i != end; ++i)
    {
        ElfSection* elfsect = (*i)->getAssocData<ElfSection>();
        assert(elfsect != 0);
        Write32(scratch, elfsect->getIndex());
    }

    group.elfsect->setSize(scratch.size());
    OutputBytes(scratch, SourceLocation());
}

void
ElfOutput::OutputSection(Section& sect, StringTable& shstrtab)
{
    BytecodeOutput* outputter = this;

    ElfSection* elfsect = sect.getAssocData<ElfSection>();
    assert(elfsect != 0);

    if (elfsect->getAlign() == 0)
        elfsect->setAlign(sect.getAlign());

    elfsect->setName(shstrtab.getIndex(sect.getName()));

    uint64_t pos;
    if (sect.isBSS())
    {
        // Don't output BSS sections.
        outputter = &m_no_output;
        pos = 0;    // position = 0 because it's not in the file
    }
    else
    {
        pos = m_os.tell();
        if (m_os.has_error())
        {
            Diag(SourceLocation(), diag::err_file_output_position);
            return;
        }

        m_fd_os.seek(elfsect->setFileOffset(pos));
        if (m_os.has_error())
        {
            Diag(SourceLocation(), diag::err_file_output_seek);
            return;
        }
    }

    // Output bytecodes
    for (Section::bc_iterator i=sect.bytecodes_begin(),
         end=sect.bytecodes_end(); i != end; ++i)
    {
        if (i->Output(*outputter))
            elfsect->AddSize(i->getTotalLen());
    }

    if (getDiagnostics().hasErrorOccurred())
        return;

    // Sanity check final section size
    assert(elfsect->getSize() == sect.bytecodes_back().getNextOffset());

    // Empty?  Go on to next section
    if (elfsect->isEmpty())
        return;

    // No relocations?  Go on to next section
    if (sect.getRelocs().size() == 0)
        return;

    // name the relocation section .rel[a].foo
    std::string relname = m_objfmt.m_config.getRelocSectionName(sect.getName());
    elfsect->setRelName(shstrtab.getIndex(relname));
}

static unsigned long
ElfAlignOutput(llvm::raw_fd_ostream& os, unsigned int align, Diagnostic& diags)
{
    assert(isExp2(align) && "requested alignment not a power of two");

    uint64_t pos = os.tell();
    if (os.has_error())
    {
        diags.Report(SourceLocation(), diag::err_file_output_position);
        return 0;
    }

    unsigned long delta = align - (pos & (align-1));
    if (delta != align)
    {
        pos += delta;
        os.seek(pos);
        if (os.has_error())
        {
            diags.Report(SourceLocation(), diag::err_file_output_seek);
            return 0;
        }
    }
    return static_cast<unsigned long>(pos);
}

void
ElfObject::Output(llvm::raw_fd_ostream& os,
                  bool all_syms,
                  DebugFormat& dbgfmt,
                  Diagnostic& diags)
{
    StringTable shstrtab, strtab;
    unsigned int align = (m_config.cls == ELFCLASS32) ? 4 : 8;

    // XXX: ugly workaround to prevent all_syms from kicking in
    if (dbgfmt.getModule().getKeyword() == "elfcfi")
        all_syms = false;

    // Add filename to strtab and set as .file symbol name
    if (m_file_elfsym)
    {
        m_file_elfsym->setName(strtab.getIndex(m_object.getSourceFilename()));
    }

    // Create .note.GNU-stack if we need to advise linker about executable
    // stack.
    Object::Config& oconfig = m_object.getConfig();
    if (oconfig.ExecStack || oconfig.NoExecStack)
    {
        Section* gnu_stack = m_object.FindSection(".note.GNU-stack");
        if (!gnu_stack)
            gnu_stack = AppendSection(".note.GNU-stack", SourceLocation(),
                                      diags);

        gnu_stack->setAlign(0);
        ElfSection* elfsect = gnu_stack->getAssocData<ElfSection>();
        if (oconfig.ExecStack)
        {
            gnu_stack->setCode(true);
            elfsect->setTypeFlags(SHT_PROGBITS, SHF_EXECINSTR);
        }
        else
        {
            gnu_stack->setCode(false);
            elfsect->setTypeFlags(SHT_PROGBITS, 0);
        }
    }

    // Allocate space for Ehdr by seeking forward
    os.seek(m_config.getProgramHeaderSize());
    if (os.has_error())
    {
        diags.Report(SourceLocation(), diag::err_file_output_seek);
        return;
    }

    // Generate version symbols.
    for (SymVers::const_iterator i=m_symvers.begin(), end=m_symvers.end();
         i != end; ++i)
    {
        SymbolRef sym = m_object.FindSymbol(i->m_real);
        if (!sym)
            continue;

        ElfSymbol& elfsym = BuildSymbol(*sym);
        llvm::SmallString<64> newname;
        if (i->m_mode == ElfSymVersion::Standard)
        {
            // rename to name@version
            std::string oldname = sym->getName();
            newname += i->m_name;
            newname += '@';
            newname += i->m_version;
            m_object.RenameSymbol(sym, newname.str());
            // if it was defined, create a new alias.
            if (sym->isDefined())
            {
                SymbolRef sym2 = m_object.getSymbol(oldname);
                ElfSymbol& elfsym2 = BuildSymbol(*sym2);
                // copy visibility and binding
                sym2->Declare(
                    static_cast<Symbol::Visibility>(sym->getVisibility()));
                elfsym2.setBinding(elfsym.getBinding());
                    sym2->DefineEqu(Expr(sym));
            }
        }
        else if (i->m_mode == ElfSymVersion::Default)
        {
            newname += i->m_name;
            newname += "@@";
            newname += i->m_version;
            SymbolRef sym2 = m_object.getSymbol(newname.str());
            ElfSymbol& elfsym2 = BuildSymbol(*sym2);
            // copy visibility and binding
            sym2->Declare(
                static_cast<Symbol::Visibility>(sym->getVisibility()));
            elfsym2.setBinding(elfsym.getBinding());
            sym2->DefineEqu(Expr(sym));
        }
        else if (i->m_mode == ElfSymVersion::Auto)
        {
            // rename to name@@version
            std::string oldname = sym->getName();
            newname += i->m_name;
            if (sym->isDefined())
                newname += "@@";
            else
                newname += '@';
            newname += i->m_version;
            m_object.RenameSymbol(sym, newname.str());
        }
    }

    m_config.secthead_count = 0;

    // dummy section header
    ElfSection null_sect(m_config, SHT_NULL, 0);
    null_sect.setIndex(m_config.secthead_count++);

    ElfOutput out(os, *this, m_object, diags);

    // Group sections.
    ElfStringIndex groupname_index = 0;
    for (Groups::iterator i=m_groups.begin(), end=m_groups.end(); i != end; ++i)
    {
        ElfGroup& group = *i;
        group.elfsect.reset(new ElfSection(m_config, SHT_GROUP, 0));

        i->sym = m_object.FindSymbol(i->name);
        ElfSymbol* elfsym = 0;
        if (i->sym)
        {
            if (groupname_index == 0)
                groupname_index = shstrtab.getIndex(".group");
            group.elfsect->setName(groupname_index);
            elfsym = i->sym->getAssocData<ElfSymbol>();
            if (!elfsym)
                elfsym = &BuildSymbol(*i->sym);
        }
        else
        {
            i->sym = m_object.getSymbol(i->name);
            group.elfsect->setName(shstrtab.getIndex(i->name));
            elfsym = &BuildSymbol(*i->sym);
            elfsym->setType(STT_SECTION);
            elfsym->setSectionIndex(m_config.secthead_count);
        }

        group.elfsect->setIndex(m_config.secthead_count++);
        group.elfsect->setEntSize(4);
        group.elfsect->setAlign(4);
    }

    // Finalize symbol table, handling any objfmt-specific extensions given
    // during parse phase.  If all_syms is true, add all local symbols and
    // include name information.
    for (Object::symbol_iterator i=m_object.symbols_begin(),
         end=m_object.symbols_end(); i != end; ++i)
    {
        FinalizeSymbol(*i, strtab, all_syms, diags);
    }

    // Number user sections (numbering required for group sections).
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        ElfSection* elfsect = i->getAssocData<ElfSection>();
        assert(elfsect != 0);
        elfsect->setIndex(m_config.secthead_count++);
    }

    // Output group sections.
    for (Groups::iterator i=m_groups.begin(), end=m_groups.end(); i != end; ++i)
    {
        out.OutputGroup(*i);
    }

    // Output user sections.
    // Assign indices and names as we go (including relocation section names).
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        out.OutputSection(*i, shstrtab);
    }

    // Go through relocations and force referenced symbols into symbol table,
    // because relocation needs a symtab index.
    for (Object::section_iterator sect=m_object.sections_begin(),
         endsect=m_object.sections_end(); sect != endsect; ++sect)
    {
        for (Section::reloc_iterator reloc=sect->relocs_begin(),
             endreloc=sect->relocs_end(); reloc != endreloc; ++reloc)
        {
            SymbolRef sym = reloc->getSymbol();
            if (!all_syms || !sym->getAssocData<ElfSymbol>())
            {
                ElfSymbol& elfsym = BuildSymbol(*sym);
                elfsym.setName(strtab.getIndex(sym->getName()));
                setSymbolSectionValue(*sym, elfsym);
                elfsym.setInTable(true);
                elfsym.Finalize(*sym, diags);
            }
        }
    }

    // Partition symbol table to put local symbols first
    stdx::stable_partition(m_object.symbols_begin(), m_object.symbols_end(),
                           isLocal);

    // Number symbols.  Start at 2 due to undefined symbol (0)
    // and file symbol (1).
    ElfSymbolIndex symtab_nlocal = 2;

    // The first symbols should be the section names in the same order as the
    // sections themselves.
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        SymbolRef sectsym = i->getSymbol();
        ElfSymbol* elfsectsym = sectsym->getAssocData<ElfSymbol>();
        elfsectsym->setSymbolIndex(symtab_nlocal++);
    }

    // The remainder of the symbols.
    m_config.AssignSymbolIndices(m_object, &symtab_nlocal);

    // Sort the symbols by symbol index.
    stdx::sort(m_object.symbols_begin(), m_object.symbols_end(), byIndex);

    unsigned long offset, size;
    ElfStringIndex shstrtab_name = shstrtab.getIndex(".shstrtab");
    ElfStringIndex strtab_name = shstrtab.getIndex(".strtab");
    ElfStringIndex symtab_name = shstrtab.getIndex(".symtab");

    // section header string table (.shstrtab)
    offset = ElfAlignOutput(os, align, diags);
    size = shstrtab.getSize();
    shstrtab.Write(os);

    ElfSection shstrtab_sect(m_config, SHT_STRTAB, 0);
    m_config.shstrtab_index = m_config.secthead_count;
    shstrtab_sect.setName(shstrtab_name);
    shstrtab_sect.setIndex(m_config.secthead_count++);
    shstrtab_sect.setFileOffset(offset);
    shstrtab_sect.setSize(size);

    // string table (.strtab)
    offset = ElfAlignOutput(os, align, diags);
    size = strtab.getSize();
    strtab.Write(os);

    ElfSection strtab_sect(m_config, SHT_STRTAB, 0);
    strtab_sect.setName(strtab_name);
    strtab_sect.setIndex(m_config.secthead_count++);
    strtab_sect.setFileOffset(offset);
    strtab_sect.setSize(size);

    // symbol table (.symtab)
    offset = ElfAlignOutput(os, align, diags);
    size = m_config.WriteSymbolTable(os, m_object, diags, out.getScratch());

    ElfSection symtab_sect(m_config, SHT_SYMTAB, 0, true);
    symtab_sect.setName(symtab_name);
    symtab_sect.setIndex(m_config.secthead_count++);
    symtab_sect.setFileOffset(offset);
    symtab_sect.setSize(size);
    symtab_sect.setInfo(symtab_nlocal);
    symtab_sect.setLink(strtab_sect.getIndex());    // link to .strtab

    // output relocations
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        // No relocations to output?  Go on to next section
        if (i->getRelocs().size() == 0)
            continue;

        ElfSection* elfsect = i->getAssocData<ElfSection>();
        assert(elfsect != 0);

        // need relocation section; set it up
        elfsect->setRelIndex(m_config.secthead_count++);
        elfsect->WriteRelocs(os, *i, out.getScratch(), *m_machine, diags);
    }

    // output section header table
    m_config.secthead_pos = ElfAlignOutput(os, 16, diags);

#if 0
    // stabs debugging support
    if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "stabs")==0)
    {
        Section* stabsect = m_object.find_section(".stab");
        Section* stabstrsect = m_object.find_section(".stabstr");
        if (stabsect && stabstrsect)
        {
            ElfSection* stab = get_elf(*stabsect);
            ElfSection* stabstr = get_elf(*stabstrsect);
            assert(stab && stabstr && "missing .stab or .stabstr section/data");
            stab->set_link(stabstr->get_index());
        }
    }
#endif

    // null section header
    null_sect.Write(os, out.getScratch());

    // group section headers
    for (Groups::iterator i=m_groups.begin(), end=m_groups.end(); i != end; ++i)
    {
        ElfGroup& group = *i;
        group.elfsect->setLink(symtab_sect.getIndex());

        ElfSymbol* elfsym = i->sym->getAssocData<ElfSymbol>();
        group.elfsect->setInfo(elfsym->getSymbolIndex());

        group.elfsect->Write(os, out.getScratch());
    }

    // user section headers
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        ElfSection* elfsect = i->getAssocData<ElfSection>();
        assert(elfsect != 0);

        elfsect->Write(os, out.getScratch());
    }

    // standard section headers
    shstrtab_sect.Write(os, out.getScratch());
    strtab_sect.Write(os, out.getScratch());
    symtab_sect.Write(os, out.getScratch());

    // relocation section headers
    for (Object::section_iterator i=m_object.sections_begin(),
         end=m_object.sections_end(); i != end; ++i)
    {
        ElfSection* elfsect = i->getAssocData<ElfSection>();
        assert(elfsect != 0);

        // relocation entries for .foo are stored in section .rel[a].foo
        elfsect->WriteRel(os, symtab_sect.getIndex(), *i, out.getScratch());
    }

    // output Ehdr
    os.seek(0);
    if (os.has_error())
    {
        diags.Report(SourceLocation(), diag::err_file_output_seek);
        return;
    }

    m_config.WriteProgramHeader(os, out.getScratch());
}

Section*
ElfObject::AddDefaultSection()
{
    Diagnostic diags(NULL);
    Section* section = AppendSection(".text", SourceLocation(), diags);
    section->setDefault(true);
    return section;
}

Section*
ElfObject::AppendSection(llvm::StringRef name,
                         SourceLocation source,
                         Diagnostic& diags)
{
    ElfSectionType type = SHT_PROGBITS;
    ElfSectionFlags flags = SHF_ALLOC;
    unsigned long align = 4;

    if (name == ".bss")
    {
        type = SHT_NOBITS;
        flags = SHF_ALLOC + SHF_WRITE;
    }
    else if (name == ".data" || name == ".ctors" || name == ".dtors")
    {
        type = SHT_PROGBITS;
        flags = SHF_ALLOC + SHF_WRITE;
    }
    else if (name == ".tdata")
    {
        type = SHT_PROGBITS;
        flags = SHF_ALLOC + SHF_WRITE + SHF_TLS;
    }
    else if (name == ".rodata")
    {
        type = SHT_PROGBITS;
        flags = SHF_ALLOC;
    }
    else if (name == ".eh_frame")
    {
        flags = SHF_ALLOC;
#ifdef __sun
        type = SHT_UNWIND;
        if (m_config.cls == ELFCLASS32)
            flags |= SHF_WRITE;
#else
        type = SHT_PROGBITS;
#endif
    }
    else if (name == ".text")
    {
        align = 16;
        type = SHT_PROGBITS;
        flags = SHF_ALLOC + SHF_EXECINSTR;
    }
    else if (name == ".comment")
    {
        align = 0;
        type = SHT_PROGBITS;
        flags = 0;
    }
    else if (name.startswith(".debug_"))
    {
        align = 0;
        type = SHT_PROGBITS;
        flags = 0;
    }
    else
    {
        // Default to code, but align=1
        align = 1;
    }

    bool code = (flags & SHF_EXECINSTR) != 0;
    bool bss = (type == SHT_NOBITS);

    Section* section = new Section(name, code, bss, source);
    m_object.AppendSection(std::auto_ptr<Section>(section));
    section->setAlign(align);

    // Define a label for the start of the section
    Location start = {&section->bytecodes_front(), 0};
    SymbolRef sym = m_object.getSymbol(name);
    if (!sym->isDefined())
    {
        sym->DefineLabel(start);
        sym->setDefSource(source);
    }
    section->setSymbol(sym);

    // Add ELF data to the section
    ElfSection* elfsect = new ElfSection(m_config, type, flags);
    section->AddAssocData(std::auto_ptr<ElfSection>(elfsect));

    return section;
}

void
ElfObject::DirGasSection(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& nvs = info.getNameValues();
    NameValues::iterator nv = nvs.begin();

    if (!nv->isString())
    {
        diags.Report(nv->getValueRange().getBegin(),
                     diag::err_value_string_or_id);
        return;
    }
    llvm::StringRef sectname = nv->getString();

    Section* sect = m_object.FindSection(sectname);
    bool first = true;
    if (sect)
        first = sect->isDefault();
    else
        sect = AppendSection(sectname, info.getSource(), diags);

    m_object.setCurSection(sect);
    sect->setDefault(false);

    ++nv;

    // No name/values, so nothing more to do
    if (nv == nvs.end())
        return;

    // Section flags must be a string.
    if (!nv->isString())
    {
        diags.Report(nv->getValueRange().getBegin(),
                     diag::err_expected_flag_string);
        return;
    }

    // Parse section flags
    ElfSection* elfsect = sect->getAssocData<ElfSection>();
    assert(elfsect != 0);

    int flags = 0, type = elfsect->getType();
    llvm::StringRef flagstr = nv->getString();

    for (size_t i=0; i<flagstr.size(); ++i)
    {
        switch (flagstr[i])
        {
            case 'a':
                flags |= SHF_ALLOC;
                break;
            case 'w':
                flags |= SHF_WRITE;
                break;
            case 'x':
                flags |= SHF_EXECINSTR;
                break;
            case 'M':
                flags |= SHF_MERGE;
                break;
            case 'S':
                flags |= SHF_STRINGS;
                break;
            case 'G':
                flags |= SHF_GROUP;
                break;
            case 'T':
                flags |= SHF_TLS;
                break;
            default:
            {
                char print_flag[2] = {flagstr[i], 0};
                diags.Report(nv->getValueRange().getBegin()
                             .getFileLocWithOffset(i),
                             diag::warn_unrecognized_section_attribute)
                    << print_flag;
            }
        }
    }
    ++nv;

    // Parse section type
    if (nv != nvs.end())
    {
        if (!nv->isToken() || nv->getToken().isNot(Token::at))
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_expected_at);
            return;
        }
        ++nv;
        if (nv == nvs.end() || !nv->isId())
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_expected_ident);
            return;
        }
        llvm::StringRef typestr = nv->getId();
        if (typestr == "progbits")
            type = SHT_PROGBITS;
        else if (typestr == "nobits")
            type = SHT_NOBITS;
        else if (typestr == "note")
            type = SHT_NOTE;
        else if (typestr == "init_array")
            type = SHT_INIT_ARRAY;
        else if (typestr == "fini_array")
            type = SHT_FINI_ARRAY;
        else if (typestr == "preinit_array")
            type = SHT_PREINIT_ARRAY;
        else if (typestr == "unwind")
            type = SHT_UNWIND;
        ++nv;
    }

    // Handle merge entity size
    if ((flags & SHF_MERGE) != 0)
    {
        if (nv == nvs.end())
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_expected_merge_entity_size);
            return;
        }

        IntNum merge;
        bool merge_ok;
        DirIntNum(*nv, diags, &m_object, &merge, &merge_ok);
        if (!merge_ok)
            return;

        elfsect->setEntSize(merge.getUInt());
        ++nv;
    }

    // Handle group name
    if ((flags & SHF_GROUP) != 0)
    {
        if (nv == nvs.end())
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_expected_group_name);
            return;
        }

        if (!nv->isString())
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_value_string_or_id);
            return;
        }

        llvm::StringMapEntry<ElfGroup*>& entry =
            m_group_map.GetOrCreateValue(nv->getString());
        ElfGroup* group;
        if (entry.getValue() != 0)
            group = entry.getValue();
        else
        {
            group = new ElfGroup;
            group->name = nv->getString();
            entry.setValue(group);
            m_groups.push_back(group);
        }
        group->sects.push_back(sect);

        ++nv;

        // look for comdat flag
        if (nv != nvs.end() && nv->isId() && nv->getId() == "comdat")
        {
            group->flags |= 1;
            ++nv;
        }

        // also treat sections named ".gnu.linkonce" as comdat
        if (sectname.startswith(".gnu.linkonce"))
            group->flags |= 1;
    }

    elfsect->setTypeFlags(static_cast<ElfSectionType>(type),
                          static_cast<ElfSectionFlags>(flags));
    sect->setBSS(type == SHT_NOBITS);
    sect->setCode((flags & SHF_EXECINSTR) != 0);
}

void
ElfObject::DirSection(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& nvs = info.getNameValues();

    NameValue& sectname_nv = nvs.front();
    if (!sectname_nv.isString())
    {
        diags.Report(sectname_nv.getValueRange().getBegin(),
                     diag::err_value_string_or_id);
        return;
    }
    llvm::StringRef sectname = sectname_nv.getString();

    Section* sect = m_object.FindSection(sectname);
    bool first = true;
    if (sect)
        first = sect->isDefault();
    else
        sect = AppendSection(sectname, info.getSource(), diags);

    m_object.setCurSection(sect);
    sect->setDefault(false);

    // No name/values, so nothing more to do
    if (nvs.size() <= 1)
        return;

    // Ignore flags if we've seen this section before
    if (!first)
    {
        diags.Report(info.getSource(), diag::warn_section_redef_flags);
        return;
    }

    // Parse section flags
    ElfSection* elfsect = sect->getAssocData<ElfSection>();
    assert(elfsect != 0);

    IntNum align;
    bool has_align = false;
    IntNum merge;
    bool has_merge = false;

    unsigned long type = elfsect->getType();
    unsigned long flags = elfsect->getFlags();

    DirHelpers helpers;
    static const struct
    {
        const char* enable;
        const char* disable;
        unsigned int flag;
    }
    name_flags[] =
    {
        {"alloc",   "noalloc",  SHF_ALLOC},
        {"exec",    "noexec",   SHF_EXECINSTR},
        {"write",   "nowrite",  SHF_WRITE},
        {"tls",     "notls",    SHF_TLS},
    };
    for (size_t i=0; i<sizeof(name_flags)/sizeof(name_flags[0]); ++i)
    {
        helpers.Add(name_flags[i].enable, false,
                    TR1::bind(&DirSetFlag, _1, _2, &flags, name_flags[i].flag));
        helpers.Add(name_flags[i].disable, false,
                    TR1::bind(&DirClearFlag, _1, _2, &flags,
                              name_flags[i].flag));
    }

    helpers.Add("noprogbits", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, SHT_NOBITS));
    helpers.Add("nobits", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, SHT_NOBITS));
    helpers.Add("progbits", false,
                TR1::bind(&DirResetFlag, _1, _2, &type, SHT_PROGBITS));

    helpers.Add("align", true, TR1::bind(&DirIntNumPower2, _1, _2, &m_object,
                                         &align, &has_align));
    helpers.Add("merge", true, TR1::bind(&DirIntNum, _1, _2, &m_object,
                                         &merge, &has_merge));

    helpers(++nvs.begin(), nvs.end(), info.getSource(), diags,
            DirNameValueWarn);

    // handle align
    if (has_align)
        sect->setAlign(align.getUInt());

    // Handle merge entity size
    if (has_merge)
    {
        flags |= SHF_MERGE;
        elfsect->setEntSize(merge.getUInt());
    }

    elfsect->setTypeFlags(static_cast<ElfSectionType>(type),
                          static_cast<ElfSectionFlags>(flags));
    sect->setBSS(type == SHT_NOBITS);
    sect->setCode((flags & SHF_EXECINSTR) != 0);
}

void
ElfObject::DirType(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& namevals = info.getNameValues();

    NameValues::iterator nv = namevals.begin(), end = namevals.end();
    SymbolRef sym = info.getObject().getSymbol(nv->getId());
    sym->Use(info.getSource());
    ++nv;

    ElfSymbol& elfsym = BuildSymbol(*sym);

    // Throw away @ sign if provided (gas syntax)
    if (nv != end && nv->isToken() && nv->getToken().is(Token::at))
        ++nv;

    // Pull new type from param
    if (nv == end || !nv->isId())
    {
        diags.Report(nv->getValueRange().getBegin(), diag::err_expected_ident);
        return;
    }

    llvm::StringRef type = nv->getId();
    if (type.equals_lower("function"))
        elfsym.setType(STT_FUNC);
    else if (type.equals_lower("object"))
        elfsym.setType(STT_OBJECT);
    else if (type.equals_lower("tls_object"))
        elfsym.setType(STT_TLS);
    else if (type.equals_lower("notype"))
        elfsym.setType(STT_NOTYPE);
    else
        diags.Report(nv->getValueRange().getBegin(),
                     diag::warn_unrecognized_symbol_type) << type;
}

void
ElfObject::DirSize(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& namevals = info.getNameValues();

    NameValue& name_nv = namevals.front();
    SymbolRef sym = info.getObject().getSymbol(name_nv.getId());
    sym->Use(name_nv.getValueRange().getBegin());

    // Pull new size from param
    if (namevals.size() < 2)
    {
        diags.Report(info.getSource(), diag::err_no_size);
        return;
    }
    NameValue& size_nv = namevals[1];
    if (!size_nv.isExpr())
    {
        diags.Report(info.getSource(), diag::err_size_expression)
            << size_nv.getValueRange();
        return;
    }
    Expr size = size_nv.getExpr(info.getObject());

    ElfSymbol& elfsym = BuildSymbol(*sym);
    elfsym.setSize(size, size_nv.getValueRange().getBegin());
}

void
ElfObject::DirWeak(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& namevals = info.getNameValues();
    for (NameValues::iterator nv = namevals.begin(), end = namevals.end();
         nv != end; ++nv)
    {
        SymbolRef sym = info.getObject().getSymbol(nv->getId());
        sym->CheckedDeclare(Symbol::GLOBAL, nv->getValueRange().getBegin(),
                            diags);

        ElfSymbol& elfsym = BuildSymbol(*sym);
        elfsym.setBinding(STB_WEAK);
    }
}

void
ElfObject::DirWeakRef(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& nvs = info.getNameValues();
    if (nvs.size() != 2 || !nvs[0].isId() || !nvs[1].isId())
    {
        diags.Report(info.getSource(), diag::err_value_id);
        return;
    }

    SymbolRef sym1 = info.getObject().getSymbol(nvs[0].getId());
    ElfSymbol& elfsym1 = BuildSymbol(*sym1);
    elfsym1.setWeakRef(true);
    SymbolRef sym2 = info.getObject().getSymbol(nvs[1].getId());
    if (!sym2->getAssocData<ElfSymbol>())
    {
        ElfSymbol& elfsym2 = BuildSymbol(*sym2);
        elfsym2.setWeakRefr(true);
    }

    sym1->CheckedDefineEqu(Expr(sym2), nvs[0].getValueRange().getBegin(),
                           diags);
}

void
ElfObject::VisibilityDir(DirectiveInfo& info,
                         Diagnostic& diags,
                         ElfSymbolVis vis)
{
    assert(info.isObject(m_object));
    NameValues& namevals = info.getNameValues();
    for (NameValues::iterator nv = namevals.begin(), end = namevals.end();
         nv != end; ++nv)
    {
        SymbolRef sym = info.getObject().getSymbol(nv->getId());
        sym->CheckedDeclare(Symbol::GLOBAL, nv->getValueRange().getBegin(),
                            diags);

        ElfSymbol& elfsym = BuildSymbol(*sym);
        elfsym.setVisibility(vis);
    }
}

void
ElfObject::DirInternal(DirectiveInfo& info, Diagnostic& diags)
{
    VisibilityDir(info, diags, STV_INTERNAL);
}

void
ElfObject::DirHidden(DirectiveInfo& info, Diagnostic& diags)
{
    VisibilityDir(info, diags, STV_HIDDEN);
}

void
ElfObject::DirProtected(DirectiveInfo& info, Diagnostic& diags)
{
    VisibilityDir(info, diags, STV_PROTECTED);
}

void
ElfObject::DirSymVer(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    NameValues& namevals = info.getNameValues();
    NameValues::iterator nv = namevals.begin(), end = namevals.end();
    llvm::StringRef real = nv->getId();
    ++nv;

    // name
    if (nv == end || !nv->isId())
    {
        diags.Report(nv->getValueRange().getBegin(), diag::err_expected_ident);
        return;
    }
    llvm::StringRef name = nv->getId();
    ++nv;

    // @, @@, @@@ portion
    int numat = 0;
    while (nv != end && nv->isToken() && nv->getToken().is(Token::at))
    {
        ++numat;
        if (numat > 3)
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_expected_ident);
            return;
        }
        ++nv;
    }

    if (numat == 0)
    {
        diags.Report(nv->getValueRange().getBegin(), diag::err_expected_at);
        return;
    }

    // version
    if (nv == end || !nv->isId())
    {
        diags.Report(nv->getValueRange().getBegin(), diag::err_expected_ident);
        return;
    }
    llvm::StringRef version = nv->getId();

    ElfSymVersion::Mode mode;
    switch (numat)
    {
        case 1: mode = ElfSymVersion::Standard; break;
        case 2: mode = ElfSymVersion::Default; break;
        case 3: mode = ElfSymVersion::Auto; break;
        default:
            assert(false && "unexpected number of @ tokens");
    }
    m_symvers.push_back(new ElfSymVersion(real, name, version, mode));
}

void
ElfObject::DirIdent(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));
    DirIdentCommon(*this, ".comment", info, diags);
}

void
ElfObject::DirVersion(DirectiveInfo& info, Diagnostic& diags)
{
    assert(info.isObject(m_object));

    // Put version data into .note section
    Section* note = info.getObject().FindSection(".note");
    if (!note)
        note = AppendSection(".note", info.getSource(), diags);

    for (NameValues::const_iterator nv=info.getNameValues().begin(),
         end=info.getNameValues().end(); nv != end; ++nv)
    {
        if (!nv->isString())
        {
            diags.Report(nv->getValueRange().getBegin(),
                         diag::err_value_string);
            continue;
        }
        llvm::StringRef str = nv->getString();
        EndianState endian;
        m_config.setEndian(endian);
        AppendData(*note, str.size(), 4, endian);   // name size
        AppendData(*note, 0, 4, endian);    // desc size
        AppendData(*note, 1, 4, endian);    // type
        AppendData(*note, str, 4, true);    // name
        // empty desc
    }
}

std::vector<llvm::StringRef>
ElfObject::getDebugFormatKeywords()
{
    static const char* keywords[] =
    {
        "null",
        "stabs",
        "elfcfi",
        "dwarf",
        "dwarfpass",
        "dwarf2",
        "dwarf2pass"
    };
    size_t keywords_size = sizeof(keywords)/sizeof(keywords[0]);
    return std::vector<llvm::StringRef>(keywords, keywords+keywords_size);
}

void
ElfObject::AddDirectives(Directives& dirs, llvm::StringRef parser)
{
    static const Directives::Init<ElfObject> nasm_dirs[] =
    {
        {"section", &ElfObject::DirSection, Directives::ARG_REQUIRED},
        {"segment", &ElfObject::DirSection, Directives::ARG_REQUIRED},
        {"type", &ElfObject::DirType, Directives::ID_REQUIRED},
        {"size", &ElfObject::DirSize, Directives::ID_REQUIRED},
        {"weak", &ElfObject::DirWeak, Directives::ID_REQUIRED},
        {"weakref", &ElfObject::DirWeakRef, Directives::ID_REQUIRED},
        {"internal", &ElfObject::DirInternal, Directives::ID_REQUIRED},
        {"hidden", &ElfObject::DirHidden, Directives::ID_REQUIRED},
        {"protected", &ElfObject::DirProtected, Directives::ID_REQUIRED},
        {"ident", &ElfObject::DirIdent, Directives::ANY},
    };
    static const Directives::Init<ElfObject> gas_dirs[] =
    {
        {".section", &ElfObject::DirGasSection, Directives::ARG_REQUIRED},
        {".type", &ElfObject::DirType, Directives::ID_REQUIRED},
        {".size", &ElfObject::DirSize, Directives::ID_REQUIRED},
        {".weak", &ElfObject::DirWeak, Directives::ID_REQUIRED},
        {".weakref", &ElfObject::DirWeakRef, Directives::ID_REQUIRED},
        {".internal", &ElfObject::DirInternal, Directives::ID_REQUIRED},
        {".hidden", &ElfObject::DirHidden, Directives::ID_REQUIRED},
        {".protected", &ElfObject::DirProtected, Directives::ID_REQUIRED},
        {".symver", &ElfObject::DirSymVer, Directives::ID_REQUIRED},
        {".ident", &ElfObject::DirIdent, Directives::ANY},
        {".version", &ElfObject::DirVersion, Directives::ARG_REQUIRED},
    };

    if (parser.equals_lower("nasm"))
        dirs.AddArray(this, nasm_dirs);
    else if (parser.equals_lower("gas") || parser.equals_lower("gnu"))
        dirs.AddArray(this, gas_dirs);
}
#if 0
static const char *elf_nasm_stdmac[] = {
    "%imacro type 1+.nolist",
    "[type %1]",
    "%endmacro",
    "%imacro size 1+.nolist",
    "[size %1]",
    "%endmacro",
    "%imacro weak 1+.nolist",
    "[weak %1]",
    "%endmacro",
    NULL
};

static const yasm_stdmac elf_objfmt_stdmacs[] = {
    { "nasm", "nasm", elf_nasm_stdmac },
    { NULL, NULL, NULL }
};
#endif
void
yasm_objfmt_elf_DoRegister()
{
    RegisterModule<ObjectFormatModule,
                   ObjectFormatModuleImpl<ElfObject> >("elf");
    RegisterModule<ObjectFormatModule,
                   ObjectFormatModuleImpl<Elf32Object> >("elf32");
    RegisterModule<ObjectFormatModule,
                   ObjectFormatModuleImpl<Elf64Object> >("elf64");
}
