IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linker notes on x86

    MaskRay发表于 2023-03-08 20:33:23
    love 0

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

    Global Offset Table

    The Global Offset Table consists of two sections:

    • .got.plt holds code addresses for PLT.
    • .got holds other addresses and offsets.

    The symbol _GLOBAL_OFFSET_TABLE_ is defined at thebeginning of the .got.plt section. .got.plthas 3 reserved entries.

    .got.plt[0] holds the link-time address of_DYNAMIC for a legacy reason. Versions of glibc prior to2.35 have the _DYNAMIC requirement. See Allabout Global Offset Table.

    .got.plt[1] and .got.plt[2] are for lazybinding PLT. Linkers communicate the address of .got.plt tortld with the dynamic tag DT_PLTGOT.

    GOT optimization

    See Allabout Global Offset Table#GOT optimization.

    Procedure Linkage Table

    Indirect Branch Tracking

    See .note.gnu.property below for Indirect BranchTracking. See All aboutProcedure Linkage Table#x86 for detail.

    The scheme used in GNU ld unnecessarily uses two sections.plt and .plt.sec. ld.lld follows suit toavoid adding complexity to tools like objdump (PLT recognition). moldadopts an alternative scheme (what Indirect Branch Tracking should haveused in the first place). Unfortunately, this is a scenario that theship has sailed and adding an alternative would not simplify the world.I think this just adds implementation complexity for other tools whichwant to support its scheme.

    Retpoline

    Retpoline is for Spectre v2 mitigation. PLT entries are synthesizedby the linker and need adaptation as well. This can be enabled in ld.lldwith -z retpolineplt.

    Thread Local Storage

    x86 uses TLS Variant II: the static TLS blocks are placed below thethread 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 Allabout thread-local storage.

    Program Property

    A .note.gnu.property section contains program propertynotes which describe special handling requirements for the linker andthe dynamic loader.

    x86 psABIs define many property notes but many don't seemparticularly useful.

    • GNU_PROPERTY_X86_ISA_1_BASELINE,GNU_PROPERTY_X86_ISA_1_V2,GNU_PROPERTY_X86_ISA_1_V2,GNU_PROPERTY_X86_ISA_1_V3: these properties describe thex86 ISA level
    • GNU_PROPERTY_X86_ISA_1_USED,GNU_PROPERTY_X86_ISA_1_NEEDED: deprecated when the x86 ISAlevel was introduced
    • GNU_PROPERTY_X86_FEATURE_1_IBT,GNU_PROPERTY_X86_FEATURE_1_SHSTK: used by Intel CET (see Control flowintegrity). See below

    For x86, the linker parses input .note.gnu.propertysections 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 theGNU_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 bitif all input .note.gnu.property sections have the bit(logical AND). -z shstk forces setting the bit without awarning.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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;

    .eh_frame

    .eh_frame sections are usually of typeSHT_PROGBITS. It is regarded as a mistake that aspecial-purpose section does not have a dedicated type. The x86-64 psABIsays that SHT_X86_64_UNWIND should be used. For futurearchitectures, it would be good not to reserve 0x70000001 for otherpurposes.

    Clang since rL252300 emits .eh_frame sections of typeSHT_X86_64_UNWIND to conform to the psABI. Linkers need toallow mixed SHT_PROGBITS and SHT_X86_64_UNWIND.eh_frame sections.

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

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

    Split stack

    gccgo uses a segmented stack scheme called "split stack". Split stackpermits a discontiguous stack which is grown automatically as needed.(The gc Go compiler now uses stack copying rather than stack splitting.Using stack copying in gccgo is difficult as the compiler needs to haveaccurate stack maps for all frames on the stack, knowing absolutelyevery pointer.)

    The scheme calls runtime functions (__morestack*)defined in libgcc and requires the linker to rewrite some codesequences.

    Each relocatable object file using split stack has a marker sectionnamed .note.GNU-split-stack. The linker uses this sectionto recognize relocatable object files compiled with split stacksupport.

    For a function call from a split-stack relocatable object file to anon-split-stack relocatable object file, the linker rewrites thefunction prologue. The prologue can be of either of the following twoschemes.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      cmp %fs:0x70,%rsp
    jae 1f
    callq __morestack
    retq
    1:
    ...

    =>

    stc
    nopl 0x0(%rax,%rax,1)
    jae 1f
    callq __morestack_non_split
    retq
    1:
    ...
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      leaq -0x100(%rsp), %r10    # The register is r10 or r11
    cmpq %fs:0x70, %r10
    jae 1f
    callq __morestack
    retq
    1:
    ...

    =>

    leaq -0x4100(%rsp), %r10 # The displacement is changed
    cmpq %fs:0x70, %r10
    jae 1f
    callq __morestack_non_split
    retq
    1:
    ...


沪ICP备19023445号-2号
友情链接