This commit adds support for embedding PIEs into the kernel, loading them
into genalloc sections, performing necessary relocations, and running code
from them. This allows platforms that need to run code from SRAM, such
an during suspend/resume, to develop that code in C instead of assembly.
Functions and data for each PIE should be grouped into sections with the
__pie(<group>) and __pie_data(<group>) macros respectively. Any symbols or
functions that are to be accessed from outside the PIE should be marked with
EXPORT_PIE_SYMBOL(<sym>). For example:
static struct ddr_timings xyz_timings __pie_data(platformxyz) = {
[...]
};
void __pie(platformxyz) xyz_ddr_on(void *addr)
{
[...]
}
EXPORT_PIE_SYMBOL(xyz_ddr_on);
While the kernel can access exported symbols from the PIE, the PIE cannot
access symbols from the kernel, but can access data from the kernel and
call functions in the kernel so long as addresses are passed into the PIE.
PIEs are loaded from the kernel into a genalloc pool with pie_load_sections.
pie_load_sections allocates space within the pool, copies the neccesary
code/data, and performs any necessary relocations. A chunk identifier is
returned for removing the PIE from the pool, and for translating symbols.
Because the PIEs are dynamically relocated, special accessors must be used
to access PIE symbols from kernel code:
- kern_to_pie(chunk, ptr): Translate a PIE symbol to the virtual address
it is loaded into within the pool.
- fn_to_pie(chunk, ptr): Same as above, but for function pointers.
- sram_to_phys(chunk, addr): Translate a virtual address within a loaded PIE
to a physical address.
Loading a PIE involves three main steps. First a set of common functions to
cover built-ins emitted by gcc (memcpy, memmove, etc) is copied into the pool.
Then the actual PIE code and data is copied into the pool. Because the PIE
code is contained within an overlay with other PIEs, offsets to the common
functions are maintained. Finally, relocations are performed as necessary.
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
241 lines
5.8 KiB
Bash
241 lines
5.8 KiB
Bash
#!/bin/sh
|
|
#
|
|
# link vmlinux
|
|
#
|
|
# vmlinux is linked from the objects selected by $(KBUILD_VMLINUX_INIT) and
|
|
# $(KBUILD_VMLINUX_MAIN). Most are built-in.o files from top-level directories
|
|
# in the kernel tree, others are specified in arch/$(ARCH)/Makefile.
|
|
# Ordering when linking is important, and $(KBUILD_VMLINUX_INIT) must be first.
|
|
#
|
|
# vmlinux
|
|
# ^
|
|
# |
|
|
# +-< $(KBUILD_VMLINUX_INIT)
|
|
# | +--< init/version.o + more
|
|
# |
|
|
# +--< $(KBUILD_VMLINUX_MAIN)
|
|
# | +--< drivers/built-in.o mm/built-in.o + more
|
|
# |
|
|
# +-< ${kallsymso} (see description in KALLSYMS section)
|
|
#
|
|
# vmlinux version (uname -v) cannot be updated during normal
|
|
# descending-into-subdirs phase since we do not yet know if we need to
|
|
# update vmlinux.
|
|
# Therefore this step is delayed until just before final link of vmlinux.
|
|
#
|
|
# System.map is generated to document addresses of all kernel symbols
|
|
|
|
# Error out on error
|
|
set -e
|
|
|
|
# Nice output in kbuild format
|
|
# Will be supressed by "make -s"
|
|
info()
|
|
{
|
|
if [ "${quiet}" != "silent_" ]; then
|
|
printf " %-7s %s\n" ${1} ${2}
|
|
fi
|
|
}
|
|
|
|
# Link of vmlinux.o used for section mismatch analysis
|
|
# ${1} output file
|
|
modpost_link()
|
|
{
|
|
${LD} ${LDFLAGS} -r -o ${1} ${KBUILD_VMLINUX_INIT} \
|
|
--start-group ${KBUILD_VMLINUX_MAIN} --end-group
|
|
}
|
|
|
|
# Link of vmlinux
|
|
# ${1} - optional extra .o files
|
|
# ${2} - output file
|
|
vmlinux_link()
|
|
{
|
|
local lds="${objtree}/${KBUILD_LDS}"
|
|
|
|
if [ "${SRCARCH}" != "um" ]; then
|
|
${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
|
|
-T ${lds} ${KBUILD_VMLINUX_INIT} \
|
|
--start-group \
|
|
${KBUILD_VMLINUX_MAIN} \
|
|
${KBUILD_VMLINUX_PIE} \
|
|
--end-group ${1}
|
|
else
|
|
${CC} ${CFLAGS_vmlinux} -o ${2} \
|
|
-Wl,-T,${lds} ${KBUILD_VMLINUX_INIT} \
|
|
-Wl,--start-group \
|
|
${KBUILD_VMLINUX_MAIN} \
|
|
${KBUILD_VMLINUX_PIE} \
|
|
-Wl,--end-group \
|
|
-lutil ${1}
|
|
rm -f linux
|
|
fi
|
|
}
|
|
|
|
|
|
# Create ${2} .o file with all symbols from the ${1} object file
|
|
kallsyms()
|
|
{
|
|
info KSYM ${2}
|
|
local kallsymopt;
|
|
|
|
if [ -n "${CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX}" ]; then
|
|
kallsymopt="${kallsymopt} --symbol-prefix=_"
|
|
fi
|
|
|
|
if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then
|
|
kallsymopt="${kallsymopt} --all-symbols"
|
|
fi
|
|
|
|
kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
|
|
|
|
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
|
|
${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
|
|
|
|
${NM} -n ${1} | \
|
|
scripts/kallsyms ${kallsymopt} | \
|
|
${CC} ${aflags} -c -o ${2} -x assembler-with-cpp -
|
|
}
|
|
|
|
# Create map file with all symbols from ${1}
|
|
# See mksymap for additional details
|
|
mksysmap()
|
|
{
|
|
${CONFIG_SHELL} "${srctree}/scripts/mksysmap" ${1} ${2}
|
|
}
|
|
|
|
sortextable()
|
|
{
|
|
${objtree}/scripts/sortextable ${1}
|
|
}
|
|
|
|
# Delete output files in case of error
|
|
trap cleanup SIGHUP SIGINT SIGQUIT SIGTERM ERR
|
|
cleanup()
|
|
{
|
|
rm -f .old_version
|
|
rm -f .tmp_System.map
|
|
rm -f .tmp_kallsyms*
|
|
rm -f .tmp_version
|
|
rm -f .tmp_vmlinux*
|
|
rm -f System.map
|
|
rm -f vmlinux
|
|
rm -f vmlinux.o
|
|
}
|
|
|
|
#
|
|
#
|
|
# Use "make V=1" to debug this script
|
|
case "${KBUILD_VERBOSE}" in
|
|
*1*)
|
|
set -x
|
|
;;
|
|
esac
|
|
|
|
if [ "$1" = "clean" ]; then
|
|
cleanup
|
|
exit 0
|
|
fi
|
|
|
|
# We need access to CONFIG_ symbols
|
|
case "${KCONFIG_CONFIG}" in
|
|
*/*)
|
|
. "${KCONFIG_CONFIG}"
|
|
;;
|
|
*)
|
|
# Force using a file from the current directory
|
|
. "./${KCONFIG_CONFIG}"
|
|
esac
|
|
|
|
#link vmlinux.o
|
|
info LD vmlinux.o
|
|
modpost_link vmlinux.o
|
|
# modpost vmlinux.o to check for section mismatches
|
|
${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o
|
|
|
|
if [ -n "${CONFIG_PIE}" ]; then
|
|
${MAKE} -f "${srctree}/scripts/Makefile.build" obj=pie
|
|
fi
|
|
|
|
# Update version
|
|
info GEN .version
|
|
if [ ! -r .version ]; then
|
|
rm -f .version;
|
|
echo 1 >.version;
|
|
else
|
|
mv .version .old_version;
|
|
expr 0$(cat .old_version) + 1 >.version;
|
|
fi;
|
|
|
|
# final build of init/
|
|
${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init
|
|
|
|
kallsymso=""
|
|
kallsyms_vmlinux=""
|
|
if [ -n "${CONFIG_KALLSYMS}" ]; then
|
|
|
|
# kallsyms support
|
|
# Generate section listing all symbols and add it into vmlinux
|
|
# It's a three step process:
|
|
# 1) Link .tmp_vmlinux1 so it has all symbols and sections,
|
|
# but __kallsyms is empty.
|
|
# Running kallsyms on that gives us .tmp_kallsyms1.o with
|
|
# the right size
|
|
# 2) Link .tmp_vmlinux2 so it now has a __kallsyms section of
|
|
# the right size, but due to the added section, some
|
|
# addresses have shifted.
|
|
# From here, we generate a correct .tmp_kallsyms2.o
|
|
# 2a) We may use an extra pass as this has been necessary to
|
|
# woraround some alignment related bugs.
|
|
# KALLSYMS_EXTRA_PASS=1 is used to trigger this.
|
|
# 3) The correct ${kallsymso} is linked into the final vmlinux.
|
|
#
|
|
# a) Verify that the System.map from vmlinux matches the map from
|
|
# ${kallsymso}.
|
|
|
|
kallsymso=.tmp_kallsyms2.o
|
|
kallsyms_vmlinux=.tmp_vmlinux2
|
|
|
|
# step 1
|
|
vmlinux_link "" .tmp_vmlinux1
|
|
kallsyms .tmp_vmlinux1 .tmp_kallsyms1.o
|
|
|
|
# step 2
|
|
vmlinux_link .tmp_kallsyms1.o .tmp_vmlinux2
|
|
kallsyms .tmp_vmlinux2 .tmp_kallsyms2.o
|
|
|
|
# step 2a
|
|
if [ -n "${KALLSYMS_EXTRA_PASS}" ]; then
|
|
kallsymso=.tmp_kallsyms3.o
|
|
kallsyms_vmlinux=.tmp_vmlinux3
|
|
|
|
vmlinux_link .tmp_kallsyms2.o .tmp_vmlinux3
|
|
|
|
kallsyms .tmp_vmlinux3 .tmp_kallsyms3.o
|
|
fi
|
|
fi
|
|
|
|
info LD vmlinux
|
|
vmlinux_link "${kallsymso}" vmlinux
|
|
|
|
if [ -n "${CONFIG_BUILDTIME_EXTABLE_SORT}" ]; then
|
|
info SORTEX vmlinux
|
|
sortextable vmlinux
|
|
fi
|
|
|
|
info SYSMAP System.map
|
|
mksysmap vmlinux System.map
|
|
|
|
# step a (see comment above)
|
|
if [ -n "${CONFIG_KALLSYMS}" ]; then
|
|
mksysmap ${kallsyms_vmlinux} .tmp_System.map
|
|
|
|
if ! cmp -s System.map .tmp_System.map; then
|
|
echo >&2 Inconsistent kallsyms data
|
|
echo >&2 Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
|
|
cleanup
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# We made a new kernel - delete old version file
|
|
rm -f .old_version
|