/*
 * Copyright (C) 2016 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 "elf_debug_writer.h"

#include <vector>

#include "base/array_ref.h"
#include "debug/dwarf/dwarf_constants.h"
#include "debug/elf_compilation_unit.h"
#include "debug/elf_debug_frame_writer.h"
#include "debug/elf_debug_info_writer.h"
#include "debug/elf_debug_line_writer.h"
#include "debug/elf_debug_loc_writer.h"
#include "debug/elf_gnu_debugdata_writer.h"
#include "debug/elf_symtab_writer.h"
#include "debug/method_debug_info.h"
#include "elf_builder.h"
#include "linker/vector_output_stream.h"

namespace art {
namespace debug {

template <typename ElfTypes>
void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
                    const ArrayRef<const MethodDebugInfo>& method_infos,
                    dwarf::CFIFormat cfi_format,
                    bool write_oat_patches) {
  // Write .strtab and .symtab.
  WriteDebugSymbols(builder, method_infos, true /* with_signature */);

  // Write .debug_frame.
  WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);

  // Group the methods into compilation units based on source file.
  std::vector<ElfCompilationUnit> compilation_units;
  const char* last_source_file = nullptr;
  for (const MethodDebugInfo& mi : method_infos) {
    if (mi.dex_file != nullptr) {
      auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
      const char* source_file = mi.dex_file->GetSourceFile(dex_class_def);
      if (compilation_units.empty() || source_file != last_source_file) {
        compilation_units.push_back(ElfCompilationUnit());
      }
      ElfCompilationUnit& cu = compilation_units.back();
      cu.methods.push_back(&mi);
      // All methods must have the same addressing mode otherwise the min/max below does not work.
      DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative);
      cu.is_code_address_text_relative = mi.is_code_address_text_relative;
      cu.code_address = std::min(cu.code_address, mi.code_address);
      cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size);
      last_source_file = source_file;
    }
  }

  // Write .debug_line section.
  if (!compilation_units.empty()) {
    ElfDebugLineWriter<ElfTypes> line_writer(builder);
    line_writer.Start();
    for (auto& compilation_unit : compilation_units) {
      line_writer.WriteCompilationUnit(compilation_unit);
    }
    line_writer.End(write_oat_patches);
  }

  // Write .debug_info section.
  if (!compilation_units.empty()) {
    ElfDebugInfoWriter<ElfTypes> info_writer(builder);
    info_writer.Start();
    for (const auto& compilation_unit : compilation_units) {
      ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
      cu_writer.Write(compilation_unit);
    }
    info_writer.End(write_oat_patches);
  }
}

std::vector<uint8_t> MakeMiniDebugInfo(
    InstructionSet isa,
    const InstructionSetFeatures* features,
    size_t rodata_size,
    size_t text_size,
    const ArrayRef<const MethodDebugInfo>& method_infos) {
  if (Is64BitInstructionSet(isa)) {
    return MakeMiniDebugInfoInternal<ElfTypes64>(isa,
                                                 features,
                                                 rodata_size,
                                                 text_size,
                                                 method_infos);
  } else {
    return MakeMiniDebugInfoInternal<ElfTypes32>(isa,
                                                 features,
                                                 rodata_size,
                                                 text_size,
                                                 method_infos);
  }
}

template <typename ElfTypes>
static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal(
    InstructionSet isa,
    const InstructionSetFeatures* features,
    const ArrayRef<const MethodDebugInfo>& method_infos) {
  std::vector<uint8_t> buffer;
  buffer.reserve(KB);
  VectorOutputStream out("Debug ELF file", &buffer);
  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
  // No program headers since the ELF file is not linked and has no allocated sections.
  builder->Start(false /* write_program_headers */);
  WriteDebugInfo(builder.get(),
                 method_infos,
                 dwarf::DW_DEBUG_FRAME_FORMAT,
                 false /* write_oat_patches */);
  builder->End();
  CHECK(builder->Good());
  return buffer;
}

std::vector<uint8_t> WriteDebugElfFileForMethods(
    InstructionSet isa,
    const InstructionSetFeatures* features,
    const ArrayRef<const MethodDebugInfo>& method_infos) {
  if (Is64BitInstructionSet(isa)) {
    return WriteDebugElfFileForMethodsInternal<ElfTypes64>(isa, features, method_infos);
  } else {
    return WriteDebugElfFileForMethodsInternal<ElfTypes32>(isa, features, method_infos);
  }
}

template <typename ElfTypes>
static std::vector<uint8_t> WriteDebugElfFileForClassesInternal(
    InstructionSet isa,
    const InstructionSetFeatures* features,
    const ArrayRef<mirror::Class*>& types)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  std::vector<uint8_t> buffer;
  buffer.reserve(KB);
  VectorOutputStream out("Debug ELF file", &buffer);
  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
  // No program headers since the ELF file is not linked and has no allocated sections.
  builder->Start(false /* write_program_headers */);
  ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
  info_writer.Start();
  ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
  cu_writer.Write(types);
  info_writer.End(false /* write_oat_patches */);

  builder->End();
  CHECK(builder->Good());
  return buffer;
}

std::vector<uint8_t> WriteDebugElfFileForClasses(InstructionSet isa,
                                                 const InstructionSetFeatures* features,
                                                 const ArrayRef<mirror::Class*>& types) {
  if (Is64BitInstructionSet(isa)) {
    return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, features, types);
  } else {
    return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, features, types);
  }
}

std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& header) {
  std::map<const char*, uint32_t> trampolines = {
    { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() },
    { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() },
    { "jniDlsymLookup", header.GetJniDlsymLookupOffset() },
    { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() },
    { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() },
    { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() },
    { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() },
  };
  std::vector<MethodDebugInfo> result;
  for (const auto& it : trampolines) {
    if (it.second != 0) {
      MethodDebugInfo info = MethodDebugInfo();
      info.trampoline_name = it.first;
      info.isa = header.GetInstructionSet();
      info.is_code_address_text_relative = true;
      info.code_address = it.second - header.GetExecutableOffset();
      info.code_size = 0;  // The symbol lasts until the next symbol.
      result.push_back(std::move(info));
    }
  }
  return result;
}

// Explicit instantiations
template void WriteDebugInfo<ElfTypes32>(
    ElfBuilder<ElfTypes32>* builder,
    const ArrayRef<const MethodDebugInfo>& method_infos,
    dwarf::CFIFormat cfi_format,
    bool write_oat_patches);
template void WriteDebugInfo<ElfTypes64>(
    ElfBuilder<ElfTypes64>* builder,
    const ArrayRef<const MethodDebugInfo>& method_infos,
    dwarf::CFIFormat cfi_format,
    bool write_oat_patches);

}  // namespace debug
}  // namespace art
