//===- lib/ReaderWriter/ELF/ArrayOrderPass.cpp ----------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ArrayOrderPass.h"

#include <algorithm>
#include <limits>

namespace lld {
namespace elf {
void ArrayOrderPass::perform(std::unique_ptr<MutableFile> &f) {
  auto definedAtoms = f->definedAtoms();

  // Move sections need to be sorted into the separate continious group.
  // That reduces a number of sorting elements and simplifies conditions
  // in the sorting predicate.
  auto last = std::stable_partition(definedAtoms.begin(), definedAtoms.end(),
                                    [](const DefinedAtom *atom) {
    if (atom->sectionChoice() != DefinedAtom::sectionCustomRequired)
      return false;

    StringRef name = atom->customSectionName();
    return name.startswith(".init_array") || name.startswith(".fini_array");
  });

  std::stable_sort(definedAtoms.begin(), last,
                   [](const DefinedAtom *left, const DefinedAtom *right) {
    StringRef leftSec = left->customSectionName();
    StringRef rightSec = right->customSectionName();

    // Drop the front dot from the section name and get
    // an optional section's number starting after the second dot.
    StringRef leftNum = leftSec.drop_front().rsplit('.').second;
    StringRef rightNum = rightSec.drop_front().rsplit('.').second;

    // Sort {.init_array, .fini_array}[.<num>] sections
    // according to their number. Sections without optional
    // numer suffix should go last.

    uint32_t leftPriority = std::numeric_limits<uint32_t>::max();
    if (!leftNum.empty())
      leftNum.getAsInteger(10, leftPriority);

    uint32_t rightPriority = std::numeric_limits<uint32_t>::max();
    if (!rightNum.empty())
      rightNum.getAsInteger(10, rightPriority);

    return leftPriority < rightPriority;
  });
}
} // end namespace elf
} // end namespace lld
