|  | /* Copyright (C) 2011-2016 Free Software Foundation, Inc. | 
|  | This file is part of the GNU C Library. | 
|  | Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011. | 
|  |  | 
|  | The GNU C Library is free software; you can redistribute it and/or | 
|  | modify it under the terms of the GNU Lesser General Public | 
|  | License as published by the Free Software Foundation; either | 
|  | version 2.1 of the License, or (at your option) any later version. | 
|  |  | 
|  | The GNU C Library is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public | 
|  | License along with the GNU C Library.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <sysdep.h> | 
|  |  | 
|  | /* Get address of "sym" in "reg" assuming r51 holds ".Llink". */ | 
|  | .macro pic_addr reg, sym | 
|  | #ifdef __tilegx__ | 
|  | moveli \reg, hw1_last(\sym - .Llink) | 
|  | shl16insli \reg, \reg, hw0(\sym - .Llink) | 
|  | ADD_PTR \reg, r51, \reg | 
|  | #else | 
|  | ADDLI_PTR \reg, r51, lo16(\sym - .Llink) | 
|  | auli \reg, \reg, ha16(\sym - .Llink) | 
|  | #endif | 
|  | .endm | 
|  |  | 
|  | .text | 
|  | ENTRY (_start) | 
|  | /* Linux starts us with sp pointing at the conventional Elf layout, | 
|  | but we need to allow two 'caller' words for our ABI convention.  */ | 
|  | { | 
|  | move r52, sp | 
|  | andi sp, sp, -8 | 
|  | } | 
|  | cfi_def_cfa_register (r52) | 
|  | { | 
|  | /* Point sp at base of ABI area; point r4 to the caller-sp word. */ | 
|  | ADDI_PTR sp, sp, -(2 * REGSIZE) | 
|  | ADDI_PTR r4, sp, -REGSIZE | 
|  | } | 
|  | { | 
|  | /* Save zero for caller sp in our 'caller' save area, and make | 
|  | sure lr has a zero value, to limit backtraces.  */ | 
|  | move lr, zero | 
|  | ST r4, zero | 
|  | } | 
|  | { | 
|  | move r0, r52 | 
|  | jal _dl_start | 
|  | } | 
|  | /* Save returned start of user program address for later. */ | 
|  | move r50, r0 | 
|  |  | 
|  | /* See if we were invoked explicitly with the dynamic loader, | 
|  | in which case we have to adjust the argument vector.  */ | 
|  | lnk r51; .Llink: | 
|  | pic_addr r4, _dl_skip_args | 
|  | LD4U r4, r4 | 
|  | BEQZT r4, .Lno_skip | 
|  |  | 
|  | /* Load the argc word at the initial sp and adjust it. | 
|  | We basically jump "sp" up over the first few argv entries | 
|  | and write "argc" a little higher up in memory, to be the | 
|  | base of the new kernel-initialized stack area.  */ | 
|  | LD_PTR r0, r52 | 
|  | { | 
|  | sub r0, r0, r4 | 
|  | SHL_PTR_ADD r52, r4, r52 | 
|  | } | 
|  | { | 
|  | ST_PTR r52, r0 | 
|  | SHL_PTR_ADD sp, r4, sp | 
|  | } | 
|  | andi sp, sp, -8 | 
|  |  | 
|  | .Lno_skip: | 
|  | /* Call_dl_init (_dl_loaded, argc, argv, envp).  See elf/start.s | 
|  | for the layout of memory here; r52 is pointing to "+0".  */ | 
|  | pic_addr r0, _rtld_local | 
|  | { | 
|  | LD_PTR r1, r52  /* load argc in r1 */ | 
|  | ADDLI_PTR r2, r52, __SIZEOF_POINTER__  /* point r2 at argv */ | 
|  | } | 
|  | { | 
|  | LD_PTR r0, r0    /* yields _rtld_global._ns_loaded */ | 
|  | addi r3, r1, 1 | 
|  | move lr, zero | 
|  | } | 
|  | { | 
|  | SHL_PTR_ADD r3, r3, r2  /* point r3 at envp */ | 
|  | jal _dl_init | 
|  | } | 
|  |  | 
|  | /* Call user program whose address we saved in r50. | 
|  | We invoke it just like a static binary, but with _dl_fini | 
|  | in r0 so we can distinguish.  */ | 
|  |  | 
|  | pic_addr r0, _dl_fini | 
|  | move lr, zero | 
|  | { | 
|  | move sp, r52 | 
|  | jr r50 | 
|  | } | 
|  |  | 
|  | /* Tell backtracer to give up (_start has no caller). */ | 
|  | info 2  /* INFO_OP_CANNOT_BACKTRACE */ | 
|  |  | 
|  | END (_start) |