// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_PROFILER_CHROME_UNWIND_INFO_ANDROID_H_
#define BASE_PROFILER_CHROME_UNWIND_INFO_ANDROID_H_

#include <stdint.h>

#include "base/base_export.h"
#include "base/containers/span.h"

namespace base {

// Represents each entry in the function table (i.e. the second level of the
// function address table).
struct FunctionTableEntry {
  // The offset into the 128kb page containing this function. Indexed by bits
  // 1-16 of the pc offset from the start of the text section.
  uint16_t function_start_address_page_instruction_offset;

  // The byte index of the first offset for the function in the function
  // offset table.
  uint16_t function_offset_table_byte_index;
};

// The header at the start of the unwind info resource, with offsets/sizes for
// the tables contained within the resource.
//
// The unwind info provides 4 tables which can translate an instruction address
// to a set of unwind instructions to unwind the function frame the instruction
// belongs to.
//
// `page_table` and `function_table` together locates which function the
// instruction address belongs to given an instruction address.
//
// `function_offset_table` and `unwind_instruction_table` together locates
// which sets of unwind instructions to execute given the function info
// obtained from `page_table` and `function_table`, and the offset between the
// instruction address and function start address.
//
// Design Doc:
// https://docs.google.com/document/d/1IYTmGCJZoiQ242xPUZX1fATD6ivsjU1TAt_fPv74ocs/edit?usp=sharing
struct BASE_EXPORT ChromeUnwindInfoHeaderAndroid {
  // The offset in bytes from the start of the unwind info resource to the
  // page table (i.e. the first level of the function address table). The page
  // table represents discrete 128kb 'pages' of memory in the text section,
  // each of which contains functions. The page table is indexed by bits 17
  // and greater of the pc offset from the start of the text section.
  // Indexing into page_table produces an index of function_table.
  uint32_t page_table_byte_offset;
  uint32_t page_table_entries;

  // The offset in bytes from the start of the unwind info resource to the
  // function table (i.e. the second level of the function address table). The
  // function table represents the individual functions within a 128kb page.
  // Each function is represented as a `FunctionTableEntry`. The relevant entry
  // for a pc offset from the start of the text section is the one with the
  // largest function_start_address_page_instruction_offset <= (pc_offset >> 1)
  // & 0xffff.
  uint32_t function_table_byte_offset;
  uint32_t function_table_entries;

  // The offset in bytes from the start of the unwind info resource to the
  // function offset table. The function offset table represents the pc
  // offsets from the start of each function along with indices into the
  // unwind instructions for the offsets. The pc offsets and unwind indices
  // are represented as (ULEB128, ULEB128) pairs in decreasing order of
  // offset. Distinct sequences of (offset, index) pairs are concatenated in
  // the table.
  uint32_t function_offset_table_byte_offset;
  uint32_t function_offset_table_size_in_bytes;

  // The offset in bytes from the start of the unwind info resource to the
  // unwind instruction table. The unwind instruction table represents
  // distinct sequences of ARM compact unwind instructions[1] used across all
  // functions in Chrome. The compact unwind instruction is a byte-oriented
  // variable length encoding so is indexed by byte position.
  // 1. See Exception handling ABI for the ARM architecture ABI, §9.3.
  // https://developer.arm.com/documentation/ihi0038/b.
  uint32_t unwind_instruction_table_byte_offset;
  uint32_t unwind_instruction_table_size_in_bytes;
};

struct BASE_EXPORT ChromeUnwindInfoAndroid {
  ChromeUnwindInfoAndroid(span<const uint8_t> unwind_instruction_table,
                          span<const uint8_t> function_offset_table,
                          span<const FunctionTableEntry> function_table,
                          span<const uint32_t> page_table);
  ~ChromeUnwindInfoAndroid();
  ChromeUnwindInfoAndroid(const ChromeUnwindInfoAndroid& other);
  ChromeUnwindInfoAndroid& operator=(const ChromeUnwindInfoAndroid& other);

  ChromeUnwindInfoAndroid(ChromeUnwindInfoAndroid&& other);
  ChromeUnwindInfoAndroid& operator=(ChromeUnwindInfoAndroid&& other);

  // Unwind instruction table is expected to have following memory layout:
  // +-----------------------------+
  // | <--1 byte--->               |
  // +-----------------------------+
  // | pop {r4, r5, lr}            | <- FUNC1 offset 10
  // +-----------------------------+
  // | add sp, 16                  | <- FUNC1 offset 4
  // +-----------------------------+
  // | mov pc, lr                  | <- FUNC1 offset 0 (COMPLETE)
  // +-----------------------------+
  // | pop {r4, r11} [byte 1/2]    | <- FUNC2 offset 8
  // +-----------------------------+
  // | pop {r4, r11} [byte 2/2]    |
  // +-----------------------------+
  // | ...                         |
  // +-----------------------------+
  // Because we are unwinding the function, the next unwind instruction to
  // execute always has smaller function offset.
  // The function offsets are often discontinuous as not all instructions in
  // the function have corresponding unwind instructions.
  //
  // See Exception handling ABI for the ARM architecture ABI, §9.3.
  // https://developer.arm.com/documentation/ihi0038/b.
  // for details in unwind instruction encoding.
  // Only following instruction encodings are handled:
  // - 00xxxxxx
  // - 01xxxxxx
  // - 1000iiii iiiiiiii
  // - 1001nnnn
  // - 10100nnn
  // - 10101nnn
  // - 10110000
  // - 10110010 uleb128
  span<const uint8_t> unwind_instruction_table;

  // Function offset table is expected to have following memory layout:
  // +---------------------+---------------------+
  // | <-----ULEB128-----> | <-----ULEB128-----> |
  // +---------------------+---------------------+
  // | Offset              | Unwind Index        |
  // +---------------------+---------------------+-----
  // | 8                   | XXX                 |  |
  // +---------------------+---------------------+  |
  // | 3                   | YYY                 |Function 1
  // +---------------------+---------------------+  |
  // | 0                   | ZZZ                 |  |
  // +---------------------+---------------------+-----
  // | 5                   | AAA                 |  |
  // +---------------------+---------------------+Function 2
  // | 0                   | BBB                 |  |
  // +---------------------+---------------------+-----
  // | ...                 | ....                |
  // +---------------------+---------------------+
  // The function offset table contains [offset, unwind index] pairs, where
  // - offset: offset from function start address of an instruction that affects
  //           the unwind state, measured in two-byte instructions.
  // - unwind index: unwind instruction location in unwind instruction table.
  //
  // Note:
  // - Each function always ends at 0 offset, which corresponds to a terminal
  //   instruction in unwind instruction table.
  // - Within each function section, offset strictly decreases. By doing so,
  //   each function's own terminal instruction will serve as termination
  //   condition when searching in the table.
  span<const uint8_t> function_offset_table;

  // The function table represents the individual functions within a 128kb page.
  // The relevant entry for an instruction offset from the start of the text
  // section is the one with the largest function_start_address_page_offset <=
  // instruction_byte_offset_from_text_section_start.
  //
  // Function table is expected to have following memory layout:
  // +--------------------+--------------------+
  // | <-----2 byte-----> | <-----2 byte-----> |
  // +--------------------+--------------------+
  // | Page Offset        | Offset Table Index |
  // +--------------------+--------------------+-----
  // | 10                 | XXX                |  |
  // +--------------------+--------------------+  |
  // | ...                | ...                |Page 0x100
  // +--------------------+--------------------+  |
  // | 65500              | ZZZ                |  |
  // +--------------------+--------------------+-----
  // | 200                | AAA                |  |
  // +--------------------+--------------------+  |
  // | ...                | ...                |Page 0x101
  // +--------------------+--------------------+  |
  // | 65535              | BBB                |  |
  // +--------------------+--------------------+-----
  //
  // Note:
  // - Within each page, `Page Offset` strictly increases.
  // - Each `FunctionTableEntry` represents a function where the start
  // address falls into the page memory address range.
  span<const FunctionTableEntry> function_table;

  // The page table represents discrete 128kb 'pages' of memory in the text
  // section, each of which contains functions. The page table is indexed by
  // bits 17 and greater of the pc offset from the start of the text section.
  // Indexing into page_table produces an index of function_table.
  //
  // The page table is expected to have following memory layout:
  // +----------------+
  // | <-- 4 byte --> |
  // +----------------+
  // | 0              |
  // +----------------+
  // | 18             |
  // +----------------+
  // | 18             |
  // +----------------+
  // | 80             |
  // +----------------+
  // | ...            |
  // +----------------+
  // Note:
  // - The page start instructions in page table non-strictly increases, i.e
  // empty page is allowed.
  span<const uint32_t> page_table;
};

// Creates `ChromeUnwindInfoAndroid` struct based on binary `data` assuming
// `data` starts with `ChromeUnwindInfoHeaderAndroid`.
BASE_EXPORT ChromeUnwindInfoAndroid
CreateChromeUnwindInfoAndroid(span<const uint8_t> data);

}  // namespace base

#endif  // BASE_PROFILER_CHROME_UNWIND_INFO_ANDROID_H_