mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-10 07:44:23 +08:00
63ecb08533
[ Upstream commita3ad84da07
] This patch future-proofs the kernel against linker changes that might put the toc pointer at some location other than .got+0x8000, by replacing __toc_start+0x8000 with .TOC. throughout. If the kernel's idea of the toc pointer doesn't agree with the linker, bad things happen. prom_init.c code relocating its toc is also changed so that a symbolic __prom_init_toc_start toc-pointer relative address is calculated rather than assuming that it is always at toc-pointer - 0x8000. The length calculations loading values from the toc are also avoided. It's a little incestuous to do that with unreloc_toc picking up adjusted values (which is fine in practice, they both adjust by the same amount if all goes well). I've also changed the way .got is aligned in vmlinux.lds and zImage.lds, mostly so that dumping out section info by objdump or readelf plainly shows the alignment is 256. This linker script feature was added 2005-09-27, available in FSF binutils releases from 2.17 onwards. Should be safe to use in the kernel, I think. Finally, put *(.got) before the prom_init.o entry which only needs *(.toc), so that the GOT header goes in the correct place. I don't believe this makes any difference for the kernel as it would for dynamic objects being loaded by ld.so. That change is just to stop lusers who blindly copy kernel scripts being led astray. Of course, this change needs the prom_init.c changes. Some notes on .toc and .got. .toc is a compiler generated section of addresses. .got is a linker generated section of addresses, generally built when the linker sees R_*_*GOT* relocations. In the case of powerpc64 ld.bfd, there are multiple generated .got sections, one per input object file. So you can somewhat reasonably write in a linker script an input section statement like *prom_init.o(.got .toc) to mean "the .got and .toc section for files matching *prom_init.o". On other architectures that doesn't make sense, because the linker generally has just one .got section. Even on powerpc64, note well that the GOT entries for prom_init.o may be merged with GOT entries from other objects. That means that if prom_init.o references, say, _end via some GOT relocation, and some other object also references _end via a GOT relocation, the GOT entry for _end may be in the range __prom_init_toc_start to __prom_init_toc_end and if the kernel does something special to GOT/TOC entries in that range then the value of _end as seen by objects other than prom_init.o will be affected. On the other hand the GOT entry for _end may not be in the range __prom_init_toc_start to __prom_init_toc_end. Which way it turns out is deterministic but a detail of linker operation that should not be relied on. A feature of ld.bfd is that input .toc (and .got) sections matching one linker input section statement may be sorted, to put entries used by small-model code first, near the toc base. This is why scripts for powerpc64 normally use *(.got .toc) rather than *(.got) *(.toc), since the first form allows more freedom to sort. Another feature of ld.bfd is that indirect addressing sequences using the GOT/TOC may be edited by the linker to relative addressing. In many cases relative addressing would be emitted by gcc for -mcmodel=medium if you appropriately decorate variable declarations with non-default visibility. The original patch is here: https://lore.kernel.org/linuxppc-dev/20210310034813.GM6042@bubble.grove.modra.org/ Signed-off-by: Alan Modra <amodra@au1.ibm.com> [aik: removed non-relocatable which is gone in24d33ac5b8
] [aik: added <=2.24 check] [aik: because of llvm-as, kernel_toc_addr() uses "mr" instead of global register variable] Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20211221055904.555763-2-aik@ozlabs.ru Stable-dep-of:1b1e380026
("powerpc: add crtsavres.o to always-y instead of extra-y") Signed-off-by: Sasha Levin <sashal@kernel.org>
418 lines
9.0 KiB
ArmAsm
418 lines
9.0 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifdef CONFIG_PPC64
|
|
#define PROVIDE32(x) PROVIDE(__unused__##x)
|
|
#else
|
|
#define PROVIDE32(x) PROVIDE(x)
|
|
#endif
|
|
|
|
#define BSS_FIRST_SECTIONS *(.bss.prominit)
|
|
#define EMITS_PT_NOTE
|
|
#define RO_EXCEPTION_TABLE_ALIGN 0
|
|
#define RUNTIME_DISCARD_EXIT
|
|
|
|
#define SOFT_MASK_TABLE(align) \
|
|
. = ALIGN(align); \
|
|
__soft_mask_table : AT(ADDR(__soft_mask_table) - LOAD_OFFSET) { \
|
|
__start___soft_mask_table = .; \
|
|
KEEP(*(__soft_mask_table)) \
|
|
__stop___soft_mask_table = .; \
|
|
}
|
|
|
|
#define RESTART_TABLE(align) \
|
|
. = ALIGN(align); \
|
|
__restart_table : AT(ADDR(__restart_table) - LOAD_OFFSET) { \
|
|
__start___restart_table = .; \
|
|
KEEP(*(__restart_table)) \
|
|
__stop___restart_table = .; \
|
|
}
|
|
|
|
#include <asm/page.h>
|
|
#include <asm-generic/vmlinux.lds.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/thread_info.h>
|
|
|
|
#define STRICT_ALIGN_SIZE (1 << CONFIG_DATA_SHIFT)
|
|
|
|
#if STRICT_ALIGN_SIZE < PAGE_SIZE
|
|
#error "CONFIG_DATA_SHIFT must be >= PAGE_SHIFT"
|
|
#endif
|
|
|
|
ENTRY(_stext)
|
|
|
|
PHDRS {
|
|
text PT_LOAD FLAGS(7); /* RWX */
|
|
note PT_NOTE FLAGS(0);
|
|
}
|
|
|
|
#ifdef CONFIG_PPC64
|
|
OUTPUT_ARCH(powerpc:common64)
|
|
jiffies = jiffies_64;
|
|
#else
|
|
OUTPUT_ARCH(powerpc:common)
|
|
jiffies = jiffies_64 + 4;
|
|
#endif
|
|
SECTIONS
|
|
{
|
|
. = KERNELBASE;
|
|
|
|
/*
|
|
* Text, read only data and other permanent read-only sections
|
|
*/
|
|
|
|
_text = .;
|
|
_stext = .;
|
|
|
|
/*
|
|
* Head text.
|
|
* This needs to be in its own output section to avoid ld placing
|
|
* branch trampoline stubs randomly throughout the fixed sections,
|
|
* which it will do (even if the branch comes from another section)
|
|
* in order to optimize stub generation.
|
|
*/
|
|
.head.text : AT(ADDR(.head.text) - LOAD_OFFSET) {
|
|
#ifdef CONFIG_PPC64
|
|
KEEP(*(.head.text.first_256B));
|
|
#ifdef CONFIG_PPC_BOOK3E
|
|
#else
|
|
KEEP(*(.head.text.real_vectors));
|
|
*(.head.text.real_trampolines);
|
|
KEEP(*(.head.text.virt_vectors));
|
|
*(.head.text.virt_trampolines);
|
|
# if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
|
|
KEEP(*(.head.data.fwnmi_page));
|
|
# endif
|
|
#endif
|
|
#else /* !CONFIG_PPC64 */
|
|
HEAD_TEXT
|
|
#endif
|
|
} :text
|
|
|
|
__head_end = .;
|
|
|
|
#ifdef CONFIG_PPC64
|
|
/*
|
|
* ALIGN(0) overrides the default output section alignment because
|
|
* this needs to start right after .head.text in order for fixed
|
|
* section placement to work.
|
|
*/
|
|
.text ALIGN(0) : AT(ADDR(.text) - LOAD_OFFSET) {
|
|
#ifdef CONFIG_LD_HEAD_STUB_CATCH
|
|
KEEP(*(.linker_stub_catch));
|
|
. = . ;
|
|
#endif
|
|
|
|
#else
|
|
.text : AT(ADDR(.text) - LOAD_OFFSET) {
|
|
ALIGN_FUNCTION();
|
|
#endif
|
|
/* careful! __ftr_alt_* sections need to be close to .text */
|
|
*(.text.hot .text.hot.* TEXT_MAIN .text.fixup .text.unlikely .text.unlikely.* .fixup __ftr_alt_* .ref.text);
|
|
#ifdef CONFIG_PPC64
|
|
*(.tramp.ftrace.text);
|
|
#endif
|
|
NOINSTR_TEXT
|
|
SCHED_TEXT
|
|
CPUIDLE_TEXT
|
|
LOCK_TEXT
|
|
KPROBES_TEXT
|
|
IRQENTRY_TEXT
|
|
SOFTIRQENTRY_TEXT
|
|
/*
|
|
* -Os builds call FP save/restore functions. The powerpc64
|
|
* linker generates those on demand in the .sfpr section.
|
|
* .sfpr gets placed at the beginning of a group of input
|
|
* sections, which can break start-of-text offset if it is
|
|
* included with the main text sections, so put it by itself.
|
|
*/
|
|
*(.sfpr);
|
|
MEM_KEEP(init.text)
|
|
MEM_KEEP(exit.text)
|
|
|
|
#ifdef CONFIG_PPC32
|
|
*(.got1)
|
|
__got2_start = .;
|
|
*(.got2)
|
|
__got2_end = .;
|
|
#endif /* CONFIG_PPC32 */
|
|
|
|
} :text
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
_etext = .;
|
|
PROVIDE32 (etext = .);
|
|
|
|
/* Read-only data */
|
|
RO_DATA(PAGE_SIZE)
|
|
|
|
#ifdef CONFIG_PPC64
|
|
SOFT_MASK_TABLE(8)
|
|
RESTART_TABLE(8)
|
|
|
|
.opd : AT(ADDR(.opd) - LOAD_OFFSET) {
|
|
__start_opd = .;
|
|
KEEP(*(.opd))
|
|
__end_opd = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__stf_entry_barrier_fixup : AT(ADDR(__stf_entry_barrier_fixup) - LOAD_OFFSET) {
|
|
__start___stf_entry_barrier_fixup = .;
|
|
*(__stf_entry_barrier_fixup)
|
|
__stop___stf_entry_barrier_fixup = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__uaccess_flush_fixup : AT(ADDR(__uaccess_flush_fixup) - LOAD_OFFSET) {
|
|
__start___uaccess_flush_fixup = .;
|
|
*(__uaccess_flush_fixup)
|
|
__stop___uaccess_flush_fixup = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__entry_flush_fixup : AT(ADDR(__entry_flush_fixup) - LOAD_OFFSET) {
|
|
__start___entry_flush_fixup = .;
|
|
*(__entry_flush_fixup)
|
|
__stop___entry_flush_fixup = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__scv_entry_flush_fixup : AT(ADDR(__scv_entry_flush_fixup) - LOAD_OFFSET) {
|
|
__start___scv_entry_flush_fixup = .;
|
|
*(__scv_entry_flush_fixup)
|
|
__stop___scv_entry_flush_fixup = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) {
|
|
__start___stf_exit_barrier_fixup = .;
|
|
*(__stf_exit_barrier_fixup)
|
|
__stop___stf_exit_barrier_fixup = .;
|
|
}
|
|
|
|
. = ALIGN(8);
|
|
__rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) {
|
|
__start___rfi_flush_fixup = .;
|
|
*(__rfi_flush_fixup)
|
|
__stop___rfi_flush_fixup = .;
|
|
}
|
|
#endif /* CONFIG_PPC64 */
|
|
|
|
#ifdef CONFIG_PPC_BARRIER_NOSPEC
|
|
. = ALIGN(8);
|
|
__spec_barrier_fixup : AT(ADDR(__spec_barrier_fixup) - LOAD_OFFSET) {
|
|
__start___barrier_nospec_fixup = .;
|
|
*(__barrier_nospec_fixup)
|
|
__stop___barrier_nospec_fixup = .;
|
|
}
|
|
#endif /* CONFIG_PPC_BARRIER_NOSPEC */
|
|
|
|
#ifdef CONFIG_PPC_FSL_BOOK3E
|
|
. = ALIGN(8);
|
|
__spec_btb_flush_fixup : AT(ADDR(__spec_btb_flush_fixup) - LOAD_OFFSET) {
|
|
__start__btb_flush_fixup = .;
|
|
*(__btb_flush_fixup)
|
|
__stop__btb_flush_fixup = .;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Various code relies on __init_begin being at the strict RWX boundary.
|
|
*/
|
|
. = ALIGN(STRICT_ALIGN_SIZE);
|
|
__srwx_boundary = .;
|
|
__init_begin = .;
|
|
|
|
/*
|
|
* Init sections discarded at runtime
|
|
*/
|
|
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {
|
|
_sinittext = .;
|
|
INIT_TEXT
|
|
|
|
/*
|
|
*.init.text might be RO so we must ensure this section ends on
|
|
* a page boundary.
|
|
*/
|
|
. = ALIGN(PAGE_SIZE);
|
|
_einittext = .;
|
|
#ifdef CONFIG_PPC64
|
|
*(.tramp.ftrace.init);
|
|
#endif
|
|
} :text
|
|
|
|
/* .exit.text is discarded at runtime, not link time,
|
|
* to deal with references from __bug_table
|
|
*/
|
|
.exit.text : AT(ADDR(.exit.text) - LOAD_OFFSET) {
|
|
EXIT_TEXT
|
|
}
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
|
|
INIT_DATA_SECTION(16)
|
|
|
|
. = ALIGN(8);
|
|
__ftr_fixup : AT(ADDR(__ftr_fixup) - LOAD_OFFSET) {
|
|
__start___ftr_fixup = .;
|
|
KEEP(*(__ftr_fixup))
|
|
__stop___ftr_fixup = .;
|
|
}
|
|
. = ALIGN(8);
|
|
__mmu_ftr_fixup : AT(ADDR(__mmu_ftr_fixup) - LOAD_OFFSET) {
|
|
__start___mmu_ftr_fixup = .;
|
|
KEEP(*(__mmu_ftr_fixup))
|
|
__stop___mmu_ftr_fixup = .;
|
|
}
|
|
. = ALIGN(8);
|
|
__lwsync_fixup : AT(ADDR(__lwsync_fixup) - LOAD_OFFSET) {
|
|
__start___lwsync_fixup = .;
|
|
KEEP(*(__lwsync_fixup))
|
|
__stop___lwsync_fixup = .;
|
|
}
|
|
#ifdef CONFIG_PPC64
|
|
. = ALIGN(8);
|
|
__fw_ftr_fixup : AT(ADDR(__fw_ftr_fixup) - LOAD_OFFSET) {
|
|
__start___fw_ftr_fixup = .;
|
|
KEEP(*(__fw_ftr_fixup))
|
|
__stop___fw_ftr_fixup = .;
|
|
}
|
|
#endif
|
|
|
|
PERCPU_SECTION(L1_CACHE_BYTES)
|
|
|
|
. = ALIGN(8);
|
|
.machine.desc : AT(ADDR(.machine.desc) - LOAD_OFFSET) {
|
|
__machine_desc_start = . ;
|
|
KEEP(*(.machine.desc))
|
|
__machine_desc_end = . ;
|
|
}
|
|
#ifdef CONFIG_RELOCATABLE
|
|
. = ALIGN(8);
|
|
.dynsym : AT(ADDR(.dynsym) - LOAD_OFFSET)
|
|
{
|
|
__dynamic_symtab = .;
|
|
*(.dynsym)
|
|
}
|
|
.dynstr : AT(ADDR(.dynstr) - LOAD_OFFSET) { *(.dynstr) }
|
|
.dynamic : AT(ADDR(.dynamic) - LOAD_OFFSET)
|
|
{
|
|
__dynamic_start = .;
|
|
*(.dynamic)
|
|
}
|
|
.hash : AT(ADDR(.hash) - LOAD_OFFSET) { *(.hash) }
|
|
.gnu.hash : AT(ADDR(.gnu.hash) - LOAD_OFFSET) { *(.gnu.hash) }
|
|
.interp : AT(ADDR(.interp) - LOAD_OFFSET) { *(.interp) }
|
|
.rela.dyn : AT(ADDR(.rela.dyn) - LOAD_OFFSET)
|
|
{
|
|
__rela_dyn_start = .;
|
|
*(.rela*)
|
|
}
|
|
#endif
|
|
/* .exit.data is discarded at runtime, not link time,
|
|
* to deal with references from .exit.text
|
|
*/
|
|
.exit.data : AT(ADDR(.exit.data) - LOAD_OFFSET) {
|
|
EXIT_DATA
|
|
}
|
|
|
|
/* freed after init ends here */
|
|
. = ALIGN(PAGE_SIZE);
|
|
__init_end = .;
|
|
|
|
/*
|
|
* And now the various read/write data
|
|
*/
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
_sdata = .;
|
|
|
|
#ifdef CONFIG_PPC32
|
|
.data : AT(ADDR(.data) - LOAD_OFFSET) {
|
|
DATA_DATA
|
|
#ifdef CONFIG_UBSAN
|
|
*(.data..Lubsan_data*)
|
|
*(.data..Lubsan_type*)
|
|
#endif
|
|
*(.data.rel*)
|
|
*(SDATA_MAIN)
|
|
*(.sdata2)
|
|
*(.got.plt) *(.got)
|
|
*(.plt)
|
|
*(.branch_lt)
|
|
}
|
|
#else
|
|
.data : AT(ADDR(.data) - LOAD_OFFSET) {
|
|
DATA_DATA
|
|
#ifdef CONFIG_UBSAN
|
|
*(.data..Lubsan_data*)
|
|
*(.data..Lubsan_type*)
|
|
#endif
|
|
*(.data.rel*)
|
|
*(.toc1)
|
|
*(.branch_lt)
|
|
}
|
|
|
|
.got : AT(ADDR(.got) - LOAD_OFFSET) ALIGN(256) {
|
|
*(.got)
|
|
#ifndef CONFIG_RELOCATABLE
|
|
__prom_init_toc_start = .;
|
|
arch/powerpc/kernel/prom_init.o*(.toc)
|
|
__prom_init_toc_end = .;
|
|
#endif
|
|
*(.toc)
|
|
}
|
|
#endif
|
|
|
|
/* The initial task and kernel stack */
|
|
INIT_TASK_DATA_SECTION(THREAD_ALIGN)
|
|
|
|
.data..page_aligned : AT(ADDR(.data..page_aligned) - LOAD_OFFSET) {
|
|
PAGE_ALIGNED_DATA(PAGE_SIZE)
|
|
}
|
|
|
|
.data..cacheline_aligned : AT(ADDR(.data..cacheline_aligned) - LOAD_OFFSET) {
|
|
CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES)
|
|
}
|
|
|
|
.data..read_mostly : AT(ADDR(.data..read_mostly) - LOAD_OFFSET) {
|
|
READ_MOSTLY_DATA(L1_CACHE_BYTES)
|
|
}
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
.data_nosave : AT(ADDR(.data_nosave) - LOAD_OFFSET) {
|
|
NOSAVE_DATA
|
|
}
|
|
|
|
BUG_TABLE
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
_edata = .;
|
|
PROVIDE32 (edata = .);
|
|
|
|
/*
|
|
* And finally the bss
|
|
*/
|
|
|
|
BSS_SECTION(0, 0, 0)
|
|
|
|
. = ALIGN(PAGE_SIZE);
|
|
_end = . ;
|
|
PROVIDE32 (end = .);
|
|
|
|
STABS_DEBUG
|
|
DWARF_DEBUG
|
|
ELF_DETAILS
|
|
|
|
DISCARDS
|
|
/DISCARD/ : {
|
|
*(*.EMB.apuinfo)
|
|
*(.glink .iplt .plt .comment)
|
|
*(.gnu.version*)
|
|
*(.gnu.attributes)
|
|
*(.eh_frame)
|
|
#ifndef CONFIG_RELOCATABLE
|
|
*(.rela*)
|
|
#endif
|
|
}
|
|
}
|