1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef NOGROD_STRING_OFFSET_TABLE_H_
18 #define NOGROD_STRING_OFFSET_TABLE_H_
19 
20 #include <cstdint>
21 
22 #include "berberis/base/bit_util.h"
23 #include "berberis/base/checks.h"
24 #include "dwarf_constants.h"
25 
26 #include "buffer.h"
27 
28 namespace nogrod {
29 
30 using berberis::bit_cast;
31 
32 // The class provides assess to .debug_str_offsets section of the elf-file
33 class StringOffsetTable {
34  public:
StringOffsetTable()35   StringOffsetTable() : format_{DwarfFormat::k32Bit} {}
StringOffsetTable(Buffer<uint8_t> buffer)36   explicit StringOffsetTable(Buffer<uint8_t> buffer)
37       : buffer_{std::move(buffer)}, format_{DetectDwarfFormat(buffer_.data(), buffer_.size())} {}
38 
39   StringOffsetTable(const StringOffsetTable&) = delete;
40   StringOffsetTable& operator=(const StringOffsetTable&) = delete;
41 
42   StringOffsetTable(StringOffsetTable&&) = default;
43   StringOffsetTable& operator=(StringOffsetTable&&) = default;
44 
45   // According to DWARF5 spec (7.26) DW_AT_str_offsets_base attribute
46   // points to the first entry following the header which is 8 for 32bit
47   // format and 16 for 64bit. We do not enforce it here, since this might
48   // not always be the case. But we do check that the base offset is greater
49   // than or equals to header size.
GetStringOffset(size_t offsets_base,size_t index)50   [[nodiscard]] uint64_t GetStringOffset(size_t offsets_base, size_t index) const {
51     constexpr const size_t k64BitHeaderSize = 16;
52     constexpr const size_t k32BitHeaderSize = 8;
53 
54     switch (format_) {
55       case DwarfFormat::k64Bit:
56         CHECK_GE(offsets_base, k64BitHeaderSize);
57         return GetOffsetAt<uint64_t>(offsets_base, index);
58       case DwarfFormat::k32Bit:
59         CHECK_GE(offsets_base, k32BitHeaderSize);
60         return GetOffsetAt<uint32_t>(offsets_base, index);
61     }
62     UNREACHABLE();
63   }
64 
65  private:
DetectDwarfFormat(const uint8_t * table,size_t size)66   static DwarfFormat DetectDwarfFormat(const uint8_t* table, size_t size) {
67     CHECK_GE(size, sizeof(uint32_t));
68     uint32_t size32 = *bit_cast<const uint32_t*>(table);
69     if (size32 == uint32_t{0xFFFF'FFFFu}) {
70       return DwarfFormat::k64Bit;
71     } else {
72       return DwarfFormat::k32Bit;
73     }
74   }
75 
76   template <typename T>
77   [[nodiscard]] T GetOffsetAt(size_t offsets_base, size_t index) const {
78     CHECK_EQ(offsets_base % sizeof(T), 0);
79     uint64_t offset = offsets_base + index * sizeof(T);
80     CHECK_LE(offset + sizeof(T), buffer_.size());
81     return *bit_cast<const T*>(buffer_.data() + offset);
82   }
83 
84   Buffer<uint8_t> buffer_;
85   DwarfFormat format_;
86 };
87 
88 }  // namespace nogrod
89 #endif  // NOGROD_STRING_OFFSET_TABLE_H_
90