Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -116,6 +116,7 @@ bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; + void relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; @@ -252,6 +253,28 @@ return 2; } +void PPC64::relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC64_TOC16_HA: + relocateOne(Loc, Type, Val); + break; + case R_PPC64_TOC16_LO_DS: { + // rewrite the instuction from a load to an addi. + uint32_t Instr = readInstrFromHalf16(Loc); + if (getPrimaryOpCode(Instr) != 58) + llvm_unreachable( + "Expeceted a 'ld' for got-indirect to toc-relative relaxing"); + Instr &= 0x3FFFFFF; + Instr |= 0x38000000; + writeInstrFromHalf16(Loc, Instr); + relocateOne(Loc, R_PPC64_TOC16_LO, Val); + } break; + default: + llvm_unreachable( + "Unexpected relocation type for got-indirect to toc-relative relaxing"); + } +} + void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: @@ -414,11 +437,12 @@ switch (Type) { case R_PPC64_TOC16: case R_PPC64_TOC16_DS: - case R_PPC64_TOC16_HA: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: - case R_PPC64_TOC16_LO_DS: return R_GOTREL; + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + return R_RELAX_TOC; case R_PPC64_TOC: return R_PPC_TOC; case R_PPC64_REL24: Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -38,7 +38,7 @@ RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; - void relaxGot(uint8_t *Loc, uint64_t Val) const override; + void relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; @@ -434,7 +434,7 @@ } template -void X86_64::relaxGot(uint8_t *Loc, uint64_t Val) const { +void X86_64::relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const { const uint8_t Op = Loc[-2]; const uint8_t ModRm = Loc[-1]; Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -611,6 +611,7 @@ return In.Got->getVA() + A - P; case R_GOTONLY_PC_FROM_END: return In.Got->getVA() + A - P + In.Got->getSize(); + case R_RELAX_TOC: case R_GOTREL: return Sym.getVA(A) - In.Got->getVA(); case R_GOTREL_FROM_END: @@ -860,6 +861,85 @@ Sec->relocateNonAlloc(Buf, Sec->template rels()); } +typedef std::pair RelaxTo; + +// Looks up the symbol and addend that a .toc indirection refers to. +template +static llvm::Optional getSymAndAddend(InputSectionBase *TocSec, + uint64_t Offset) { + auto Relas = TocSec->template relas(); + // Check that the Offset maps to a valid relocation. + size_t Index = Offset / /* Toc Entry size */ 8; + if (Index >= Relas.size()) { + error("Invalid offset " + Twine(Offset) + " into the .toc section in " + + toString(TocSec->File)); + return Optional(); + } + + // Map the offset to an entry in the .toc. The entires are expected to be in + // ascending order, with one for each .toc entry so we don't have to search + // through all relocations to find the one with the correct offset. + auto Rela = Relas[Offset / 8 /* Toc Entry size */]; + if (Offset != Rela.r_offset) { + warn("Offset mismatch on toc relaxation in " + toString(TocSec->File) + + " expected: " + Twine(Offset) + " but found: " + Twine(Rela.r_offset)); + return Optional(); + } + + // If the target symbol is defined then return it and the addend. + Symbol &IndirectSym = TocSec->getFile()->getRelocTargetSym(Rela); + if (Defined *DefSym = dyn_cast_or_null(&IndirectSym)) + return Optional({DefSym, getAddend(Rela)}); + + // Can't relax if the target symbol is not defined. + return Optional(); +} + +// Determine if relaxing a toc-indirection is safe. Returns the symbol + addend +// pair in the case that relaxation is valid. +static llvm::Optional relaxTo(Relocation Rel) { + // If the symbol the relocation refers to is not the .toc section + // this can't be a toc-indirection. + Defined *DefSym = dyn_cast(Rel.Sym); + if (!DefSym || !DefSym->isSection() || + !(DefSym->Section->Name.compare(".toc") == 0)) + return Optional(); + + if (Rel.Addend < 0) + return Optional(); + + auto *TocISB = dyn_cast(DefSym->Section); + // Get the symbol + addend that the toc-indirection refers to. + return Config->IsLE ? getSymAndAddend(TocISB, Rel.Addend) + : getSymAndAddend(TocISB, Rel.Addend); +} + +static bool tryRelaxToc(InputFile *File, RelType Type, const Relocation &Rel, + RelExpr Expr, uint8_t *BufLoc, uint64_t AddrLoc) { + if (!Config->TocOptimize) + return false; + + auto CanRelaxTo = relaxTo(Rel); + if (!CanRelaxTo.hasValue()) + return false; + + RelaxTo RelaxTo = CanRelaxTo.getValue(); + uint64_t TargetVA = SignExtend64( + getRelocTargetVA(File, Type, /* Addend */ RelaxTo.second, AddrLoc, + /* Defined* */ *RelaxTo.first, Expr), + Config->Wordsize * 8); + // Because we only have the 2 instructions of the got-indirect access sequence + // to rewrite we can only relax if the symbol definition is within 32 bits of + // the TOC base-pointer. For medium code model this is guaranteed but in the + // large code model parts of the data segment will be too far away to relax. + if (!isInt<32>(TargetVA - getPPC64TocBase())) + return false; + + // Safe to relax, let the target perform the actual relaxation. + Target->relaxGot(BufLoc, Type, TargetVA); + return true; +} + void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { assert(Flags & SHF_ALLOC); const unsigned Bits = Config->Wordsize * 8; @@ -880,7 +960,13 @@ switch (Expr) { case R_RELAX_GOT_PC: case R_RELAX_GOT_PC_NOPIC: - Target->relaxGot(BufLoc, TargetVA); + Target->relaxGot(BufLoc, Type, TargetVA); + break; + case R_RELAX_TOC: + // If the relocation is relaxable tryTocRelax will perform the relxation + // and return true. Otherwise relocate to the original .toc entry. + if (!tryRelaxToc(File, Type, Rel, Expr, BufLoc, AddrLoc)) + Target->relocateOne(BufLoc, Type, TargetVA); break; case R_RELAX_TLS_IE_TO_LE: Target->relaxTlsIeToLe(BufLoc, Type, TargetVA); Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ ELF/Relocations.h @@ -67,6 +67,7 @@ R_PPC_TOC, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, + R_RELAX_TOC, R_RELAX_TLS_GD_TO_IE, R_RELAX_TLS_GD_TO_IE_ABS, R_RELAX_TLS_GD_TO_IE_END, Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -342,7 +342,7 @@ static bool isRelExpr(RelExpr Expr) { return isRelExprOneOf(Expr); + R_RELAX_GOT_PC, R_RELAX_TOC>(Expr); } // Returns true if a given relocation can be computed at link-time. @@ -361,7 +361,7 @@ R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, - R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, + R_GOTONLY_PC_FROM_END, R_PLT_PC, R_RELAX_TOC, R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT, R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT, R_TLSLD_HINT, R_TLSIE_HINT>(E)) @@ -1003,7 +1003,7 @@ // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. if (isRelExprOneOf(Expr)) + R_GOTREL_FROM_END, R_PPC_TOC, R_RELAX_TOC>(Expr)) In.Got->HasGotOffRel = true; // Read an addend. Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -131,7 +131,7 @@ virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const; - virtual void relaxGot(uint8_t *Loc, uint64_t Val) const; + virtual void relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const; virtual void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const; virtual void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const; virtual void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -150,7 +150,7 @@ return Expr; } -void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const { +void TargetInfo::relaxGot(uint8_t *Loc, RelType Type, uint64_t Val) const { llvm_unreachable("Should not have claimed to be relaxable"); } Index: test/ELF/Inputs/ppc64-global.s =================================================================== --- /dev/null +++ test/ELF/Inputs/ppc64-global.s @@ -0,0 +1,8 @@ + .type glob,@object + .data + .global glob + .p2align 2 +glob: + .long 55 + .size glob, 4 + Index: test/ELF/Inputs/ppc64-local.s =================================================================== --- /dev/null +++ test/ELF/Inputs/ppc64-local.s @@ -0,0 +1,7 @@ + .type local_sym,@object + .data + .global local_sym + .p2align 2 +local_sym: + .long 55 + .size local_sym, 4 Index: test/ELF/ppc64-func-entry-points.s =================================================================== --- test/ELF/ppc64-func-entry-points.s +++ test/ELF/ppc64-func-entry-points.s @@ -75,6 +75,6 @@ // CHECK: foo_external_diff: // CHECK-NEXT: 10010080: {{.*}} addis 2, 12, 2 // CHECK-NEXT: 10010084: {{.*}} addi 2, 2, 32640 -// CHECK-NEXT: 10010088: {{.*}} nop +// CHECK-NEXT: 10010088: {{.*}} addis 5, 2, -1 // CHECK: foo_external_same: // CHECK-NEXT: 100100b0: {{.*}} add 3, 4, 3 Index: test/ELF/ppc64-got-indirect.s =================================================================== --- test/ELF/ppc64-got-indirect.s +++ test/ELF/ppc64-got-indirect.s @@ -2,13 +2,17 @@ # RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o # RUN: llvm-readobj -relocations %t.o | FileCheck -check-prefix=RELOCS-LE %s -# RUN: ld.lld %t.o -o %t2 +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-global.s -o %t1.o +# RUN: ld.lld -shared -o %t1.so %t1.o +# RUN: ld.lld %t.o %t1.so -o %t2 # RUN: llvm-objdump -D %t2 | FileCheck %s --check-prefix=CHECK-LE # RUN: llvm-objdump -D %t2 | FileCheck %s # RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o # RUN: llvm-readobj -relocations %t.o | FileCheck -check-prefix=RELOCS-BE %s -# RUN: ld.lld %t.o -o %t2 +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %p/Inputs/ppc64-global.s -o %t1.o +# RUN: ld.lld -shared -o %t1.so %t1.o +# RUN: ld.lld %t.o %t1.so -o %t2 # RUN: llvm-objdump -D %t2 | FileCheck %s --check-prefix=CHECK-BE # RUN: llvm-objdump -D %t2 | FileCheck %s @@ -42,13 +46,6 @@ .section .toc,"aw",@progbits .LC0: .tc glob[TC],glob - .type glob,@object # @glob - .data - .globl glob - .p2align 2 -glob: - .long 55 # 0x37 - .size glob, 4 # Verify the relocations emitted for glob are through the .toc @@ -77,39 +74,30 @@ # Verify that the global variable access is done through the correct # toc entry: -# r2 = .TOC. = 0x10038000. -# r3 = r2 - 32760 = 0x10030008 -> .toc entry for glob. +# r2 = .TOC. = 0x100280c0 +# r3 = *(r2 - 32760) --> (r2 - 32760) = 0x100280c8 -> .toc entry for glob. # CHECK: _start: -# CHECK-NEXT: 10010000: {{.*}} addis 2, 12, 3 -# CHECK-NEXT: 10010004: {{.*}} addi 2, 2, -32768 +# CHECK-NEXT: 10010000: {{.*}} addis 2, 12, 2 +# CHECK-NEXT: 10010004: {{.*}} addi 2, 2, -32576 # CHECK-NEXT: 10010008: {{.*}} nop # CHECK-NEXT: 1001000c: {{.*}} ld 3, -32760(2) # CHECK: 1001001c: {{.*}} lwa 3, 0(3) -# CHECK-LE: Disassembly of section .data: -# CHECK-LE-NEXT: glob: -# CHECK-LE-NEXT: 10020000: 37 00 00 00 - # CHECK-LE: Disassembly of section .got: # CHECK-LE-NEXT: .got: -# CHECK-LE-NEXT: 10030000: 00 80 03 10 -# CHECK-LE-NEXT: 10030004: 00 00 00 00 +# CHECK-LE-NEXT: 100200c0: c0 80 02 10 +# CHECK-LE-NEXT: 100200c4: 00 00 00 00 # Verify that .toc comes right after .got # CHECK-LE: Disassembly of section .toc: -# CHECK-LE: 10030008: 00 00 02 10 - -# CHECK-BE: Disassembly of section .data: -# CHECK-BE-NEXT: glob: -# CHECK-BE-NEXT: 10020000: 00 00 00 37 +# CHECK-LE: 100200c8: # CHECK-BE: Disassembly of section .got: # CHECK-BE-NEXT: .got: -# CHECK-BE-NEXT: 10030000: 00 00 00 00 -# CHECK-BE-NEXT: 10030004: 10 03 80 00 +# CHECK-BE-NEXT: 100200c0: 00 00 00 00 +# CHECK-BE-NEXT: 100200c4: 10 02 80 c0 # Verify that .toc comes right after .got # CHECK-BE: Disassembly of section .toc: -# CHECK-BE: 10030008: 00 00 00 00 -# CHECK-BE: 1003000c: 10 02 00 00 +# CHECK-BE: 100200c8: Index: test/ELF/ppc64-relocs.s =================================================================== --- test/ELF/ppc64-relocs.s +++ test/ELF/ppc64-relocs.s @@ -1,12 +1,12 @@ # REQUIRES: ppc # RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t -# RUN: ld.lld %t -o %t2 +# RUN: ld.lld --no-toc-optimize %t -o %t2 # RUN: llvm-objdump -D %t2 | FileCheck %s --check-prefix=DATALE # RUN: llvm-objdump -D %t2 | FileCheck %s # RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t -# RUN: ld.lld %t -o %t2 +# RUN: ld.lld --no-toc-optimize %t -o %t2 # RUN: llvm-objdump -D %t2 | FileCheck %s --check-prefix=DATABE # RUN: llvm-objdump -D %t2 | FileCheck %s @@ -63,7 +63,7 @@ # CHECK: Disassembly of section .R_PPC64_TOC16_HA: # CHECK: .FR_PPC64_TOC16_HA: -# CHECK: 10010018: {{.*}} nop +# CHECK: 10010018: {{.*}} addis 1, 2, 0 .section .R_PPC64_REL24,"ax",@progbits .globl .FR_PPC64_REL24 @@ -160,8 +160,8 @@ # 0x10000190 + 0xfeb4 = 0x10010044 # CHECK: Disassembly of section .R_PPC64_REL32: # CHECK: .FR_PPC64_REL32: -# CHECK: 1001003c: {{.*}} nop -# CHECK: 10010040: {{.*}} ld 5, -32736(2) +# CHECK: 1001003c: {{.*}} addis 5, 2, 0 +# CHECK: 10010040: {{.*}} ld 5, -32736(5) # CHECK: 10010044: {{.*}} add 3, 3, 4 .section .R_PPC64_REL64, "ax",@progbits Index: test/ELF/ppc64-toc-relax-jumptable.s =================================================================== --- /dev/null +++ test/ELF/ppc64-toc-relax-jumptable.s @@ -0,0 +1,79 @@ +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objdump -D %t | FileCheck %s +# RUN: llvm-objdump -D %t | FileCheck --check-prefix=LE %s + +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objdump -D %t | FileCheck %s +# RUN: llvm-objdump -D %t | FileCheck --check-prefix=BE %s + + +# Verify that the load from the .toc section was relaxed to an +# add of an offset to the TOC base-pointer (calculating the address +# of the jump table rather then loading the address from the .toc). + +# CHECK: Disassembly of section .rodata: +# CHECK-NEXT: .rodata: +# CHECK-NEXT: 10000190 + +# CHECK-LABEL: _start +# CHECK: clrldi 3, 3, 62 +# CHECK-NEXT: addis 4, 2, -2 +# CHECK-NEXT: addi 4, 4, -32368 +# CHECK-NEXT: sldi 3, 3, 2 + +# LE: Disassembly of section .toc: +# LE-NEXT: .toc: +# LE-NEXT: 10020008: 90 01 00 10 +# LE-NEXT: 1002000c: 00 00 00 00 + +# BE: Disassembly of section .toc: +# BE-NEXT: .toc: +# BE-NEXT: 10020008: 00 00 00 00 +# BE-NEXT: 1002000c: 10 00 01 90 + + .text + .global _start + .type _start, @function +_start: +.Lstart_gep: + addis 2, 12, .TOC.-.Lstart_gep@ha + addi 2, 2, .TOC.-.Lstart_gep@l +.Lstart_lep: + .localentry _start, .Lstart_lep-.Lstart_gep + rldicl 3, 3, 0, 62 + addis 4, 2, .LJTI_TE@toc@ha + ld 4, .LJTI_TE@toc@l(4) + sldi 3, 3, 2 + lwax 3, 3, 4 + add 3, 3, 4 + mtctr 3 + bctr + +.LBB1: + li 3, 0 + blr +.LBB2: + li 3, 10 + blr +.LBB3: + li 3, 55 + blr +.LBB4: + li 3, 255 + blr + + .section .rodata,"a",@progbits + .p2align 2 +.LJT: + .long .LBB1-.LJT + .long .LBB2-.LJT + .long .LBB3-.LJT + .long .LBB4-.LJT + +.section .toc,"aw",@progbits +# TOC entry for the jumptable address. +.LJTI_TE: + .tc .LJT[TC],.LJT + Index: test/ELF/ppc64-toc-relax.s =================================================================== --- /dev/null +++ test/ELF/ppc64-toc-relax.s @@ -0,0 +1,72 @@ +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-global.s -o %t1.shared.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-local.s -o %t2.o +# RUN: ld.lld -shared %t1.shared.o -o %t.so +# RUN: ld.lld %t.o %t2.o %t.so -o %t +# RUN: llvm-objdump -d %t | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %p/Inputs/ppc64-global.s -o %t1.shared.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %p/Inputs/ppc64-local.s -o %t2.o +# RUN: ld.lld -shared %t1.shared.o -o %t.so +# RUN: ld.lld %t.o %t2.o %t.so -o %t +# RUN: llvm-objdump -d %t | FileCheck %s + + .abiversion 2 + .text + + .global should_relax + .type should_relax, @function + + .global can_not_relax + .type can_not_relax, @function + + .global section_sym_relax + .type section_sym_relax, @function + +# Compiler emits a .toc entry for the symbol becuase it is not defined in this +# compilation unit. A definition will get linked in from another object file +# making it locally defined and so relaxable from a got-indirect access to a +# toc-relative access. +should_relax: +.Lshould_relax_gep: + addis 2, 12, .TOC.-.Lshould_relax_gep@ha + addi 2, 2, .TOC.-.Lshould_relax_gep@l +.Lshould_relax_lep: + .localentry should_relax, .Lshould_relax_lep-.Lshould_relax_gep + addis 3, 2, .LTE1@toc@ha + ld 3, .LTE1@toc@l(3) + lwa 3, 0(3) + blr + +# CHECK-LABEL: should_relax +# CHECK-NEXT: 10010000: +# CHECK: 10010008: {{.*}} addis 3, 2, -2 +# CHECK-NEXT: 1001000c: {{.*}} addi 3, 3, 32576 +# CHECK-NEXT: 10010010: {{.*}} lwa 3, 0(3) + +# Compiler emits a .toc entry for the symbol becuase it is not defined in this +# compilation unit. A definition will get linked in from a shared-object making +# it unrelaxable. +can_not_relax: +.Lcan_not_relax_gep: + addis 2, 12, .TOC.-.Lcan_not_relax_gep@ha + addi 2, 2, .TOC.-.Lcan_not_relax_gep@l +.Lcan_not_relax_lep: + .localentry can_not_relax, .Lcan_not_relax_lep-.Lcan_not_relax_gep + addis 3, 2, .LTE2@toc@ha + ld 3, .LTE2@toc@l(3) + lwa 3, 0(3) + blr + +#CHECK-LABEL: can_not_relax: +# CHECK-NEXT: 10010018: +# CHECK: 10010020: {{.*}} nop +# CHECK-NEXT: 10010024: {{.*}} ld 3, -32752(2) +# CHECK-NEXT: 10010028: {{.*}} lwa 3, 0(3) + + .section .toc,"aw",@progbits +.LTE1: + .tc local_sym[TC], local_sym +.LTE2: + .tc glob[TC], glob