3

Linker notes on x86

 1 year ago
source link: https://maskray.me/blog/2023-02-19-linker-notes-on-x86
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

This article describes target-specific things about x86 in ELF linkers. I will use "x86" to refer to both x86-32 and x86-64.

Global Offset Table

_GLOBAL_OFFSET_TABLE_ is defined at the start of the section .got.plt. .got.plt has 3 reserved entries.

_GLOBAL_OFFSET_TABLE_[0] stores the link-time address of _DYNAMIC for a legacy reason. It's unused now. See All about Global Offset Table.

_GLOBAL_OFFSET_TABLE_[1] and _GLOBAL_OFFSET_TABLE_[2] are for lazy binding PLT.

GOT optimization

See All about Global Offset Table#GOT optimization.

Procedure Linkage Table

Retpoline and Indirect Branch Tracking

ld.lld supports -z retpolineplt for Spectre v2 mitigation.

See .note.gnu.property below for Indirect Branch Tracking.

See All about Procedure Linkage Table#x86 for detail.

Thread Local Storage

x86 uses TLS Variant II: the static TLS blocks are placed below the thread pointer.

Beside the traditional general dynamic and local dynamic TLS models, there are TLSDESC ABIs for x86-32 and x86-64.

The linker performs TLS optimization.

See All about thread-local storage.

.note.gnu.property

The linker parses input .note.gnu.property sections and recognize -z force-ibt and -z shstk to compute the output .note.gnu.property (type is SHT_NOTE) section.

The following code (extracted from ld.lld) describes the behavior. Basically, without extra options, the output has the GNU_PROPERTY_X86_FEATURE_1_IBT bit if all input .note.gnu.property sections have the bit (logical AND). -z force-ibt forces setting the bit with a warning.

The output has the GNU_PROPERTY_X86_FEATURE_1_SHSK bit if all input .note.gnu.property sections have the bit (logical AND). -z shstk forces setting the bit without a warning.

for (ELFFileBase *f : ctx.objectFiles) {
uint32_t features = f->andFeatures;
if (!(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
if (config->zCetReport == "error")
error(toString(f) + ": -z cet-report: file does not have GNU_PROPERTY_X86_FEATURE_1_IBT property");
else if config->zCetReport == "warning")
warn(toString(f) + ": -z cet-report: file does not have GNU_PROPERTY_X86_FEATURE_1_IBT property");
}
if (!(features & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) {
if (config->zCetReport == "error")
error(toString(f) + ": -z cet-report: file does not have GNU_PROPERTY_X86_FEATURE_1_SHSTK property");
else if config->zCetReport == "warning")
warn(toString(f) + ": -z cet-report: file does not have GNU_PROPERTY_X86_FEATURE_1_SHSTK property");
}

if (config->zForceIbt && !(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
if (config->zCetReport == "none")
warn(toString(f) + ": -z force-ibt: file does not have "
"GNU_PROPERTY_X86_FEATURE_1_IBT property");
features |= GNU_PROPERTY_X86_FEATURE_1_IBT;
}
ret &= features;
}

// Force enable Shadow Stack.
if (config->zShstk)
ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;

See Control flow integrity for an overview of Intel CET.

.eh_frame

Clang since rL252300 emits .eh_frame sections of type SHT_X86_64_UNWIND. It is unfortunate that .eh_frame does not use a dedicated section type but nowadays

.gnu.linkonce.t.__x86.get_pc_thunk.bx

The magic symbol prefix .gnu.linkonce was used before COMDAT was introduced into ELF. .gnu.linkonce was very obsoleted now, but unfortunately .gnu.linkonce.t.__x86.get_pc_thunk.bx remained relevant in glibc x86-32 until glibc 2.32 (2020-08).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK