xref: /freebsd/contrib/llvm-project/lld/MachO/EhFrame.h (revision 4530e0c3e78d0616367d37273d6c1f47f627839b)
1 //===- EhFrame.h ------------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLD_MACHO_EH_FRAME_H
10 #define LLD_MACHO_EH_FRAME_H
11 
12 #include "InputSection.h"
13 #include "Relocations.h"
14 
15 #include "lld/Common/LLVM.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/ADT/PointerUnion.h"
18 #include "llvm/ADT/SmallVector.h"
19 
20 /*
21  * NOTE: The main bulk of the EH frame parsing logic is in InputFiles.cpp as it
22  * is closely coupled with other file parsing logic; EhFrame.h just contains a
23  * few helpers.
24  */
25 
26 /*
27  * === The EH frame format ===
28  *
29  * EH frames can either be Common Information Entries (CIEs) or Frame
30  * Description Entries (FDEs). CIEs contain information that is common amongst
31  * several FDEs. Each FDE contains a pointer to its CIE. Thus all the EH frame
32  * entries together form a forest of two-level trees, with CIEs as the roots
33  * and FDEs as the leaves. Note that a CIE must precede the FDEs which point
34  * to it.
35  *
36  * A CIE comprises the following fields in order:
37  * 1.   Length of the entry (4 or 12 bytes)
38  * 2.   CIE offset (4 bytes; always 0 for CIEs)
39  * 3.   CIE version (byte)
40  * 4.   Null-terminated augmentation string
41  * 5-8. LEB128 values that we don't care about
42  * 9.   Augmentation data, to be interpreted using the aug string
43  * 10.  DWARF instructions (ignored by LLD)
44  *
45  * An FDE comprises of the following:
46  * 1. Length of the entry (4 or 12 bytes)
47  * 2. CIE offset (4 bytes pcrel offset that points backwards to this FDE's CIE)
48  * 3. Function address (pointer-sized pcrel offset)
49  * 4. (Optional) Augmentation data length
50  * 5. (Optional) LSDA address (pointer-sized pcrel offset)
51  * 6. DWARF instructions (ignored by LLD)
52  */
53 namespace lld {
54 namespace macho {
55 
56 class EhReader {
57 public:
58   EhReader(const ObjFile *file, ArrayRef<uint8_t> data, size_t dataOff)
59       : file(file), data(data), dataOff(dataOff) {}
60   size_t size() const { return data.size(); }
61   // Read and validate the length field.
62   uint64_t readLength(size_t *off) const;
63   // Skip the length field without doing validation.
64   void skipValidLength(size_t *off) const;
65   uint8_t readByte(size_t *off) const;
66   uint32_t readU32(size_t *off) const;
67   uint64_t readPointer(size_t *off, uint8_t size) const;
68   StringRef readString(size_t *off) const;
69   void skipLeb128(size_t *off) const;
70   void failOn(size_t errOff, const Twine &msg) const;
71 
72 private:
73   const ObjFile *file;
74   ArrayRef<uint8_t> data;
75   // The offset of the data array within its section. Used only for error
76   // reporting.
77   const size_t dataOff;
78 };
79 
80 // The EH frame format, when emitted by llvm-mc, consists of a number of
81 // "abs-ified" relocations, i.e. relocations that are implicitly encoded as
82 // pcrel offsets in the section data. The offsets refer to the locations of
83 // symbols in the input object file. When we ingest these EH frames, we convert
84 // these implicit relocations into explicit Relocs.
85 //
86 // These pcrel relocations are semantically similar to X86_64_RELOC_SIGNED_4.
87 // However, we need this operation to be cross-platform, and ARM does not have a
88 // similar relocation that is applicable. We therefore use the more verbose (but
89 // more generic) subtractor relocation to encode these pcrel values. ld64
90 // appears to do something similar -- its `-r` output contains these explicit
91 // subtractor relocations.
92 class EhRelocator {
93 public:
94   EhRelocator(InputSection *isec) : isec(isec) {}
95 
96   // For the next two methods, let `PC` denote `isec address + off`.
97   // Create relocs writing the value of target - PC to PC.
98   void makePcRel(uint64_t off,
99                  llvm::PointerUnion<Symbol *, InputSection *> target,
100                  uint8_t length);
101   // Create relocs writing the value of PC - target to PC.
102   void makeNegativePcRel(uint64_t off,
103                          llvm::PointerUnion<Symbol *, InputSection *> target,
104                          uint8_t length);
105   // Insert the new relocations into isec->relocs.
106   void commit();
107 
108 private:
109   InputSection *isec;
110   // Insert new relocs here so that we don't invalidate iterators into the
111   // existing relocs vector.
112   SmallVector<Reloc, 6> newRelocs;
113 };
114 
115 } // namespace macho
116 } // namespace lld
117 
118 #endif
119