Table of Contents

binutils support for Xtensa

FDPIC support

FDPIC instruction sequences

Local call

Default local call

+0:     movi    tmp, target@GOT
+3:     add     tmp, tmp, localGOTptr
+5:     l32i    tmp, tmp, 0
+7:     mov     GOTptr, localGOTptr
+9:     callx0  tmp

No-GOT local call

+0:     movi    tmp1, target
+3:     l32i    tmp2, localGOTptr, TEXT_SEGMENT_OFFSET
+5:     add     tmp1, tmp1, tmp2
+7:     mov     GOTptr, localGOTptr
+9:     callx0  tmp1

When call0 reaches the target it can be transformed to

+0:     mov     GOTptr, localGOTptr
+2:     call0   target

In the j.l style it could probably be done like

        call0.l target, tmp1, tmp2, localGOTptr

PLT call

Obvious version:

+0:     movi    tmp, target@PLTGOT
+3:     add     tmp, tmp, localGOTptr
+5:     l32i    tmp, tmp, 0
+7:     l32i    GOTptr, tmp, 4
+9:     l32i    tmp, tmp, 0
+11:    callx0  tmp
target@PLT:
+0:     movi    a8, target@PLTGOT
+3:     add     a8, a8, GOTptr
+5:     movi    a9, target-symbol
+8:     l32i    a10, GOTptr, RESOLVER_FN
+10:    l32i    GOTptr, GOTptr, RESOLVER_GOT
+12:    jx0     a10

The inline part calls the PLT part only once, after resolution the inline part calls the target directly. The adjustment is done to a single GOT entry, so it's atomic. The inline part can be reduced to a fixed direct call to the PLT:

+0:     mov     GOTptr, localGOTptr
+2:     call0   target@PLT

that reduces the inline part from 14 to 5 bytes, but adds two jumps to each call and some special logic to the resolver to avoid name resolution on each call.

TLS Support

TLS General Dynamic

+0:     movi    tmp1, x@GOTTLSDESC
+3:     add     arg0, tmp1, localGOTptr         # TLS_ARG
+5:     l32i    tmp2, arg0, 0                   # TLS_FUNCDESC
+7:     l32i    GOTptr, tmp2, 4                 # TLS_GOT
+9:     _l32i   tmp3, tmp2, 0                   # TLS_FUNC
+12:    callx0  tmp3                            # TLS_CALL

This TLSDESC is not the same as the descriptor of the default xtensa toolchain. It contains two pointers, one to the resolver function, the other to that other descriptor containing DTPOFF and module index in the dtv.

TLS Local Dynamic

Header getting the address of the _TLS_MODULE_BASE_ is the same as in General Dynamic, or a possible one instruction less version:

+0:     l32i    arg0, localGOTptr, _TLS_MODULE_BASE_DESC_OFF
+2:     l32i    tmp1, arg0, 0
+4:     l32i    GOTptr, tmp1, 4
+6:     _l32i   tmp2, tmp1, 0
+9:     callx0  tmp2
...
+m:     movi    tmp3, x@DTPOFF
+m+3:   add     res, tmp3, rv0
...

_TLS_MODULE_BASE_DESC_OFF is a small fixed offset (16?) from the GOT base where an entry with R_XTENSA_TLSDESC(_TLS_MODULE_BASE_) relocation against it is placed.

TLS Initial Exec

+0:     movi    tmp1, x@GOTTPOFF
+3:     add     tmp2, tmp1, localGOTptr         # TLS_TPOFF_PTR
+5:     l32i    tmp3, tmp2, 0                   # TLS_TPOFF_LOAD
+7:     rur     tmp4, THREADPTR
+10:    add     res, tmp3, tmp4

TLS Local Exec

+0:     movi    tmp1, x@TPOFF
+3:     rur     tmp2, THREADPTR
+6:     add     res, tmp1, tmp2

Linker optimizations

General Dynamic -> Initial Exec

+0:     movi    tmp1, x@GOTTLSDESC                                              movi    tmp1, x@GOTTPOFF
+3:     add     arg0, tmp1, localGOTptr         # TLS_ARG                       add     arg0, tmp1, localGOTptr
+5:     l32i    tmp2, arg0, 0                   # TLS_FUNCDESC                  l32i    arg0, arg0, 0
+7:     l32i    GOTptr, tmp2, 4                 # TLS_GOT                       nop
+9:     _l32i   tmp3, tmp2, 0                   # TLS_FUNC                      rur     tmp3, THREADPTR
+12:    callx0  tmp3                            # TLS_CALL                      add     arg0, arg0, tmp3

General Dynamic -> Local Exec

+0:     movi    tmp1, x@GOTTLSDESC                                              movi    tmp1, x@TPOFF
+3:     add     arg0, tmp1, localGOTptr         # TLS_ARG                       mov     arg0, tmp1
+5:     l32i    tmp2, arg0, 0                   # TLS_FUNCDESC                  nop
+7:     l32i    GOTptr, tmp2, 4                 # TLS_GOT                       nop
+9:     _l32i   tmp3, tmp2, 0                   # TLS_FUNC                      rur     tmp3, THREADPTR
+12:    callx0  tmp3                            # TLS_CALL                      add     arg0, arg0, tmp3

Initial Exec -> Local Exec

+0:     movi    tmp1, x@GOTTPOFF                                                movi    tmp1, x@TPOFF
+3:     add     tmp2, tmp1, localGOTptr         # TLS_TPOFF_PTR                 mov     tmp2, tmp1
+5:     l32i    tmp3, tmp2, 0                   # TLS_TPOFF_LOAD                mov     tmp3, tmp2
+7:     rur     tmp4, THREADPTR                                                 rur     tmp4, THREADPTR
+10:    add     res, tmp3, tmp4                                                 add     res, tmp3, tmp4

Manual toolchain building script

#! /bin/bash -ex

target=${TARGET:-xtensa-linux-uclibcfdpic}
build_base=`pwd`/build
src_base=$(dirname $(readlink -f "$0"))
binutils_src=$HOME/ws/tensilica/binutils-gdb/binutils-gdb
gcc_src=$HOME/ws/tensilica/gcc/gcc
linux_src="$src_base/linux"
uclibc_src="$src_base/uclibc-ng"
uclibc_config_src="$src_base/uclibc-ng-config"

prefix=`pwd`
sysroot="$prefix/$target/sysroot"
linux_headers="$sysroot/usr"

_FLAGS_FOR_HOST=${FLAGS_FOR_HOST:--Og -g}
_FLAGS_FOR_TARGET=${FLAGS_FOR_TARGET:--mauto-litpools -mfdpic -Oz -g}
CROSS_COMPILE=${CROSS_COMPILE:-$prefix/bin/$target-}
TARGET_CFLAGS="$_FLAGS_FOR_TARGET -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64"

if [ "$1" = "-r" ]; then
        reconfigure=1
fi

mkdir -p .build

mkdir .build/binutils && (
        cd .build/binutils
        "$binutils_src/configure" --prefix="$prefix" \
                --target=$target \
                --with-sysroot="$sysroot" \
                --disable-shared --disable-werror --disable-gdb --disable-gdbstub \
                CFLAGS="$_FLAGS_FOR_HOST"

        make -j8
        make -j8 install
)

mkdir .build/gcc-initial && (
        cd .build/gcc-initial
        "$gcc_src/configure" --prefix="$prefix" \
                --target=$target \
                --with-sysroot="$sysroot" \
                --enable-languages=c \
                --disable-shared \
                --enable-__cxa_atexit \
                --disable-tls --disable-threads \
                --without-headers --with-newlib \
                CFLAGS_FOR_TARGET="$_FLAGS_FOR_TARGET" \
                CXXFLAGS_FOR_TARGET="$_FLAGS_FOR_TARGET" \
                CFLAGS="$_FLAGS_FOR_HOST" \
                CXXFLAGS="$_FLAGS_FOR_HOST"

        make -j8 all-gcc
        make -j8 all-target-libgcc
        make -j8 install-gcc
        make -j8 install-target-libgcc
)

mkdir .build/linux && (
        cd .build/linux
        make -C "$linux_src" ARCH=xtensa \
                CROSS_COMPILE="$CROSS_COMPILE" O=`pwd` \
                defconfig   
        make -C "$linux_src" ARCH=xtensa \
                CROSS_COMPILE="$CROSS_COMPILE" O=`pwd` \
                INSTALL_HDR_PATH="$linux_headers" \
                -j8 headers_install
)

mkdir .build/uclibc && (
        cd .build/uclibc
        cp "$uclibc_config_src/.config" .
        if [ -n "$reconfigure" ]; then
                make -C "$uclibc_src" ARCH=xtensa \
                        CROSS_COMPILE="$CROSS_COMPILE" \
                        O=`pwd` KERNEL_HEADERS="$linux_headers/include" \
                        UCLIBC_EXTRA_CFLAGS="${TARGET_CFLAGS}" \
                        menuconfig
                cp .config "$uclibc_config_src"
        fi

        make -C "$uclibc_src" ARCH=xtensa \
                CROSS_COMPILE="$CROSS_COMPILE" \
                O=`pwd` KERNEL_HEADERS="$linux_headers/include" \
                UCLIBC_EXTRA_CFLAGS="${TARGET_CFLAGS}" \
                -j8 "$@"
        make -C "$uclibc_src" ARCH=xtensa \
                CROSS_COMPILE="$CROSS_COMPILE" \
                O=`pwd` KERNEL_HEADERS="$linux_headers/include" \
                UCLIBC_EXTRA_CFLAGS="${TARGET_CFLAGS}" \
                DESTDIR="$sysroot" \
                install
)

mkdir .build/gcc-final && ( 
        cd .build/gcc-final 
        "$gcc_src/configure" --prefix="$prefix" \
                --target=$target \
                --with-sysroot="$sysroot" \
                --enable-languages=c,c++ \
                --disable-shared \
                --enable-__cxa_atexit \
                --disable-tls --disable-threads \
                --with-uclibc \
                CFLAGS_FOR_TARGET="$_FLAGS_FOR_TARGET" \
                CXXFLAGS_FOR_TARGET="$_FLAGS_FOR_TARGET" \
                CFLAGS="$_FLAGS_FOR_HOST" \
                CXXFLAGS="$_FLAGS_FOR_HOST"

        make -j8 all
        make -j8 install
)