airs-notes/eh_frame_hdr.md

50 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# .eh_frame_hdr
If you followed my last post, you will see that in order to unwind the stack
you have to find the FDE associated with a given program counter value. There
are two steps to this problem. The first one is finding the CIEs and FDEs at
all. The second one is, given the set of FDEs, finding the one you need.
The old way this worked was that gcc would create a global constructor which
called the function `__register_frame_info`, passing a pointer to the
`.eh_frame` data and a pointer to the object. The latter pointer would indicate
the shared library, and was used to deregister the information after a dlclose.
When looking for an FDE, the unwinder would walk through the registered frames,
and sort them. Then it would use the sorted list to find the desired FDE.
The old way still works, but these days, at least on GNU/Linux, the sorting is
done at link time, which is better than doing it at runtime. Both gold and the
GNU linker support an option `--eh-frame-hdr` which tell them to construct a
header for all the .eh_frame sections. This header is placed in a section named
.eh_frame_hdr and also in a PT_GNU_EH_FRAME segment. At runtime the unwinder
can find all the `PT_GNU_EH_FRAME` segments by calling `dl_iterate_phdr`.
The format of the `.eh_frame_hdr` section is as follows:
* A 1 byte version number, currently 1.
* A 1 byte encoding of the pointer to the exception frames. This is a
`DW_EH_PE_xxx` value. It is normally `DW_EH_PE_pcrel | DW_EH_PE_sdata4`,
meaning a 4 byte relative offset.
* A 1 byte encoding of the count of the number of FDEs in the lookup table.
This is a `DW_EH_PE_xxx` value. It is normally `DW_EH_PE_udata4`, meaning a 4
byte unsigned count.
* A 1 byte encoding of the entries in the lookup table. This is a
`DW_EH_PE_xxx` value. It is normally `DW_EH_PE_datarel | DW_EH_PE_sdata4`,
meaning a 4 byte offset from the start of the `.eh_frame_hdr` section. That
is the only encoding that gccs current unwind library supports.
* A pointer to the contents of the `.eh_frame` section, encoded as indicated by
the second byte in the header. This pointer is only used if the format of the
lookup table is not supported or is for some reason omitted..
* The number of FDE pointers in the table, encoded as indicated by the third
byte in the header. If there are no FDEs, the encoding can be `DW_EH_PE_omit`
and this number will not be present.
* The lookup table itself, starting at a 4-byte aligned address in memory.
Assuming the fourth byte in the header is `DW_EH_PE_datarel | DW_EH_PE_sdata4`,
each entry in the table is 8 bytes long. The first four bytes are an offset
to the initial PC value for the FDE. The last four byte are an offset to the
FDE data itself. The table is sorted by starting PC.
Since FDEs do not overlap, this table is sufficient for the stack unwinder to
quickly find the relevant FDE if there is one.