glibc/sysdeps/powerpc/dl-machine.h
Ulrich Drepper e61abf8398 Update.
1997-03-19 01:40  Ulrich Drepper  <drepper@cygnus.com>

	* sysdeps/unix/sysv/sco3.2.4/Dist: New file.

	* sysdeps/unix/sysv/sysv4/Dist: Add __getpgid.c and __setpgid.c.

	* sysdeps/unix/bsd/Dist: Add bsdstat.h, setrgid.c, and setruid.c.

	* sysdeps/unix/sysv/Dist: Add direct.h.

	* sysdeps/unix/sysv/linux/Dist: Add netinet/tcp.h.

	* Make-dist ($(tardir).tar): Prefer writing temporary file to
	$TMPDIR is available.  The default is /tmp.

	* sysdeps/generic/ip.h: Move to...
	* sysdeps/generic/netinet/ip.h: ...here.

	* Makefile (tests): Quote $(CC) argument to isomac program.
	Patch by H.J. Lu <hjl@gnu.ai.mit.edu>.

	* sysdeps/i386/setjmp.S (__setjmp): Fix fatal bug where 0 argument
	is placed in wrong place on the stack.
	Reported by Marc Lehmann <mlehmann@hildesheim.sgh-net.de>.
	* sysdeps/tst-setjmp.c: Add new test for above problem.

	* sysdeps/libm-i387/e_pow.S: Compute PIC addres early.
	* sysdeps/libm-i387/e_powf.S: Likewise.
	* sysdeps/libm-i387/e_powl.S: Likewise.

1997-03-18 23:18  Ulrich Drepper  <drepper@cygnus.com>

	* time/offtime.c (__offtime): Change type of `yg' to long int.
	Reported by a sun <asun@zoology.washington.edu>.

1997-03-18 23:08  a sun  <asun@zoology.washington.edu>

	* sysdeps/unix/sysv/linux/net/if_ppp.h (PPP_VERSION): Define to
	2.2.0 to prevent version mismatch.

1997-03-17 19:26  Andreas Jaeger  <aj@arthur.pfalz.de>

	* stdio-common/printf_fphex.c (MIN): Only define MIN if not
	already defined.

1997-03-14 23:34  Geoff Keating  <geoffk@ozemail.com.au>

	* sysdeps/unix/sysv/linux/powerpc/termbits.h: Leave ioctl numbers
	in ioctls.h.

	* elf/rtld.c (_dl_start): Call elf_machine_runtime_setup when the
	loader first relocates itself.
	* sysdeps/powerpc/elf/start.c (__start1): Fix bug for static objects.
	* sysdeps/powerpc/dl-machine.h (elf_machine_rela): Fix bugs in
	jump slot relocation. Prefer relative branches (some PowerPC chips
	don't predict absolute branches).
	(elf_machine_runtime_setup): Simplify and correct expressions.
	(RTLD_START): Fix bug changing _dl_starting_up.
	* sysdeps/unix/sysv/linux/powerpc/dl-sysdep.c: Added. Deal with
	strange Linux/PPC padding of initial stack.

1997-03-11 04:14  Geoff Keating  <geoffk@ozemail.com.au>

	* sysdeps/unix/sysv/linux/powerpc/termbits.h: Increase NCCS to 39,
	for future expansion.
	* sysdeps/unix/sysv/linux/powerpc/sys/kernel_termios.h: Added.
	* sysdeps/powerpc/dl-machine.h (elf_machine_rela): Explain why it
	can't have a switch statement.
	* sysdeps/powerpc/elf/start.c (__start1): Explain why it can't be
	static.

	* sysdeps/powerpc/elf/start.c (_start): Use .previous to avoid
	confusing gcc's idea of the current section.
	* sysdeps/powerpc/dl-machine.h (ELF_MACHINE_RUNTIME_TRAMPOLINE,
	RTLD_START): Likewise.

1997-03-08 09:10  Geoff Keating  <geoffk@ozemail.com.au>

	* sysdeps/powerpc/dl-machine.h (elf_machine_rela,
	elf_machine_runtime_setup): Flush data & instruction caches when
	necessary, for 603/604 support. Add better support for large PLTs.
	(elf_machine_rela): Remove relocations that wouldn't work if
	anyone ever used them. Use memcpy for copy reloc, it'll be safe.
	Never target branch relocations at a PLT entry.

	* sysdeps/powerpc/bsd-setjmp.S: Make jump to PLT entry if we are
	generating PIC.
	* sysdeps/powerpc/bsd-_setjmp.S: Likewise.
	* sysdeps/powerpc/setjmp.S: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/clone.S: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/socket.S: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/syscall.S: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/sysdep.h: Likewise.

	* sysdeps/powerpc/elf/start.c: Clean up.

	* sysdeps/powerpc/__longjmp.S: Return 'value' as result from
	setjmp call.

	* sysdeps/unix/sysv/linux/powerpc/statbuf.h: New file.

1997-03-09 12:36  H.J. Lu  <hjl@gnu.ai.mit.edu>

	* Make-dist (srcs): Add $(test-srcs).
	* MakeTAGS (all-sources): Likewise.
	* Makerules (depfiles, common-mostlyclean): Likewise.
	* Rules (tests): Likewise.

1997-03-18 05:28  Roland McGrath  <roland@frob.com>

	* elf/dl-reloc.c (RESOLVE): Don't try to resolve ocal symbols.

1997-03-17 21:39  Philip Blundell  <phil@london.uk.eu.org>

	* nis/nss_nis/nis-service.c (_nss_nis_getservbyname_r): Allow
	protocol=NULL to match any protocol rather than returning an
	error.

1997-03-17 19:00  Philip Blundell  <phil@london.uk.eu.org>

	* nss/nss_files/files-service.c (servbyname): Match any protocol
	if proto==NULL.

1997-03-18 05:17  Ulrich Drepper  <drepper@cygnus.com>

	* sysdeps/unix/sysv/linux/alpha/fcntlbits.h: Don't define O_NORW.
	* sysdeps/unix/sysv/linux/fcntlbits.h: Likewise.
	Proposed by Thomas Bushnell, n/BSG.

1997-03-18 07:53  H.J. Lu  <hjl@gnu.ai.mit.edu>

	* sysdeps/generic/setenv.c (setenv): Don't copy name when we reuse
	the buffer for replacement.

1997-03-16 19:30  H.J. Lu  <hjl@gnu.ai.mit.edu>

	* sysdeps/unix/sysv/linux/syscalls.list: Add sys_fstat,
	sys_lstat and sys_stat.

1997-03-17 12:43  Thorsten Kukuk  <kukuk@vt.uni-paderborn.de>

	Add NIS+ functions
	* shlib-versions: Add libnss_nisplus.
	* nis/Makefile: Add NIS+ source files.
	* nis/nis_call.c: New file.
	* nis/nis_clone.c: New file.
	* nis/nis_error.c: New file.
	* nis/nis_file.c: New file.
	* nis/nis_free.c: New file.
	* nis/nis_intern.c: New file.
	* nis/nis_intern.h: New file.
	* nis/nis_local_names.c: New file.
	* nis/nis_names.c: New file.
	* nis/nis_print.c: New file.
	* nis/nis_server.c: New file.
	* nis/nis_subr.c: New file.
	* nis/nis_table.c: New file.
	* nis/nis_xdr.c: New file.
	* nis/nss-nisplus.h: New file.
	* nis/nss_nisplus/nisplus-alias.c: New file.
	* nis/nss_nisplus/nisplus-ethers.c: New file.
	* nis/nss_nisplus/nisplus-grp.c: New file.
	* nis/nss_nisplus/nisplus-hosts.c: New file.
	* nis/nss_nisplus/nisplus-netgrp.c: New file.
	* nis/nss_nisplus/nisplus-network.c: New file.
	* nis/nss_nisplus/nisplus-proto.c: New file.
	* nis/nss_nisplus/nisplus-publickey.c: New file.
	* nis/nss_nisplus/nisplus-pwd.c: New file.
	* nis/nss_nisplus/nisplus-rpc.c: New file.
	* nis/nss_nisplus/nisplus-service.c: New file.
	* nis/nss_nisplus/nisplus-spwd.c: New file.
	* nis/rpcsvc/nis.h: New file.
	* nis/rpcsvc/nis.x: New file.
	* nis/rpcsvc/nis_object.x: New file.
	* nis/rpcsvc/nis_tags.h: New file.
	* nis/rpcsvc/nislib.h: New file.

1997-03-17 12:52  Thomas Bushnell, n/BSG  <thomas@gnu.ai.mit.edu>

	* mach/devstream.c (output/write_some): Don't try and write more
	than IO_INBAND_MAX in a single call to device_write_inband.

	* sysdeps/libm-ieee754/w_atan2.c: Don't ignore exception if library
	* sysdeps/libm-ieee754/w_atan2f.c: Likewise.
	* sysdeps/libm-ieee754/w_atan2l.c: Likewise.
	* sysdeps/unix/sysv/linux/sys/mman.h (msync): Add description for
	* stdlib/atoll.c: Undefine atoll, not atol.
1997-03-19 05:47:56 +00:00

564 lines
18 KiB
C

/* Machine-dependent ELF dynamic relocation inline functions. PowerPC version.
Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define ELF_MACHINE_NAME "powerpc"
#include <assert.h>
#include <string.h>
#include <link.h>
/* stuff for the PLT */
#define PLT_INITIAL_ENTRY_WORDS 18
#define PLT_LONGBRANCH_ENTRY_WORDS 10
#define PLT_DOUBLE_SIZE (1<<13)
#define PLT_ENTRY_START_WORDS(entry_number) \
(PLT_INITIAL_ENTRY_WORDS + (entry_number)*2 + \
((entry_number) > PLT_DOUBLE_SIZE ? \
((entry_number) - PLT_DOUBLE_SIZE)*2 : \
0))
#define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries)
#define OPCODE_ADDI(rd,ra,simm) \
(0x38000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff)
#define OPCODE_ADDIS(rd,ra,simm) \
(0x3c000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff)
#define OPCODE_ADD(rd,ra,rb) \
(0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11)
#define OPCODE_B(target) (0x48000000 | (target) & 0x03fffffc)
#define OPCODE_BA(target) (0x48000002 | (target) & 0x03fffffc)
#define OPCODE_BCTR() 0x4e800420
#define OPCODE_LWZ(rd,d,ra) \
(0x80000000 | (rd) << 21 | (ra) << 16 | (d) & 0xffff)
#define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21)
#define OPCODE_RLWINM(ra,rs,sh,mb,me) \
(0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1)
#define OPCODE_LI(rd,simm) OPCODE_ADDI(rd,0,simm)
#define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh)
#define PPC_DCBST(where) asm __volatile__ ("dcbst 0,%0" : : "r"(where))
+#define PPC_SYNC asm __volatile__ ("sync")
+#define PPC_ISYNC asm __volatile__ ("sync; isync")
+#define PPC_ICBI(where) asm __volatile__ ("icbi 0,%0" : : "r"(where))
/* Use this when you've modified some code, but it won't be in the
instruction fetch queue (or when it doesn't matter if it is). */
#define MODIFIED_CODE_NOQUEUE(where) \
do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0)
/* Use this when it might be in the instruction queue. */
#define MODIFIED_CODE(where) \
do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0)
/* Return nonzero iff E_MACHINE is compatible with the running host. */
static inline int
elf_machine_matches_host (Elf32_Half e_machine)
{
return e_machine == EM_PPC;
}
/* Return the link-time address of _DYNAMIC, stored as
the first value in the GOT. */
static inline Elf32_Addr
elf_machine_dynamic (void)
{
Elf32_Addr *got;
asm (" bl _GLOBAL_OFFSET_TABLE_-4@local"
: "=l"(got));
return *got;
}
/* Return the run-time load address of the shared object. */
static inline Elf32_Addr
elf_machine_load_address (void)
{
unsigned *got;
unsigned *branchaddr;
/* This is much harder than you'd expect. Possibly I'm missing something.
The 'obvious' way:
Apparently, "bcl 20,31,$+4" is what should be used to load LR
with the address of the next instruction.
I think this is so that machines that do bl/blr pairing don't
get confused.
asm ("bcl 20,31,0f ;"
"0: mflr 0 ;"
"lis %0,0b@ha;"
"addi %0,%0,0b@l;"
"subf %0,%0,0"
: "=b" (addr) : : "r0", "lr");
doesn't work, because the linker doesn't have to (and in fact doesn't)
update the @ha and @l references; the loader (which runs after this
code) will do that.
Instead, we use the following trick:
The linker puts the _link-time_ address of _DYNAMIC at the first
word in the GOT. We could branch to that address, if we wanted,
by using an @local reloc; the linker works this out, so it's safe
to use now. We can't, of course, actually branch there, because
we'd cause an illegal instruction exception; so we need to compute
the address ourselves. That gives us the following code: */
/* Get address of the 'b _DYNAMIC@local'... */
asm ("bl 0f ;"
"b _DYNAMIC@local;"
"0:"
: "=l"(branchaddr));
/* ... and the address of the GOT. */
asm (" bl _GLOBAL_OFFSET_TABLE_-4@local"
: "=l"(got));
/* So now work out the difference between where the branch actually points,
and the offset of that location in memory from the start of the file. */
return (Elf32_Addr)branchaddr - *got +
(*branchaddr & 0x3fffffc |
(int)(*branchaddr << 6 & 0x80000000) >> 6);
}
#define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) /* nothing */
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
LOADADDR is the load address of the object; INFO is an array indexed
by DT_* of the .dynamic section info. */
#ifdef RESOLVE
static inline void
elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
const Elf32_Sym *sym, const struct r_found_version *version)
{
Elf32_Addr *const reloc_addr = (Elf32_Addr *)(map->l_addr + reloc->r_offset);
Elf32_Word loadbase, finaladdr;
const int rinfo = ELF32_R_TYPE (reloc->r_info);
if (rinfo == R_PPC_NONE)
return;
assert (sym != NULL);
if (ELF32_ST_TYPE (sym->st_info) == STT_SECTION ||
rinfo == R_PPC_RELATIVE)
{
/* Has already been relocated. */
loadbase = map->l_addr;
finaladdr = loadbase + reloc->r_addend;
}
else
{
int flags;
/* We never want to use a PLT entry as the destination of a
reloc, when what is being relocated is a branch. This is
partly for efficiency, but mostly so we avoid loops. */
if (rinfo == R_PPC_REL24 ||
rinfo == R_PPC_ADDR24 ||
rinfo == R_PPC_JMP_SLOT)
flags = DL_LOOKUP_NOPLT;
else if (rinfo == R_PPC_COPY)
flags = DL_LOOKUP_NOEXEC;
else
flags = 0;
loadbase = (Elf32_Word) (char *) (RESOLVE (&sym, version, flags));
if (sym == NULL)
{
/* Weak symbol that wasn't actually defined anywhere. */
assert(loadbase == 0);
finaladdr = reloc->r_addend;
}
else
finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value +
reloc->r_addend);
}
/* This is an if/else if chain because GCC 2.7.2.[012] turns case
statements into non-PIC table lookups. When a later version
comes out that fixes this, this should be changed. */
if (rinfo == R_PPC_ADDR16_LO)
{
*(Elf32_Half*) reloc_addr = finaladdr;
}
else if (rinfo == R_PPC_ADDR16_HI)
{
*(Elf32_Half*) reloc_addr = finaladdr >> 16;
}
else if (rinfo == R_PPC_ADDR16_HA)
{
*(Elf32_Half*) reloc_addr = finaladdr + 0x8000 >> 16;
}
else if (rinfo == R_PPC_REL24)
{
Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
assert (delta << 6 >> 6 == delta);
*reloc_addr = *reloc_addr & 0xfc000003 | delta & 0x3fffffc;
}
else if (rinfo == R_PPC_UADDR32 ||
rinfo == R_PPC_GLOB_DAT ||
rinfo == R_PPC_ADDR32 ||
rinfo == R_PPC_RELATIVE)
{
*reloc_addr = finaladdr;
}
else if (rinfo == R_PPC_ADDR24)
{
assert (finaladdr << 6 >> 6 == finaladdr);
*reloc_addr = *reloc_addr & 0xfc000003 | finaladdr & 0x3fffffc;
}
else if (rinfo == R_PPC_COPY)
{
/* Memcpy is safe to use here, because ld.so doesn't have any
COPY relocs (it's self-contained). */
memcpy (reloc_addr, (char *) finaladdr, sym->st_size);
}
else if (rinfo == R_PPC_REL32)
{
*reloc_addr = finaladdr - (Elf32_Word) (char *) reloc_addr;
}
else if (rinfo == R_PPC_JMP_SLOT)
{
Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
if (delta << 6 >> 6 == delta)
*reloc_addr = OPCODE_B(delta);
else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
*reloc_addr = OPCODE_BA(finaladdr);
else
{
Elf32_Word *plt = (Elf32_Word *)((char *)map->l_addr +
map->l_info[DT_PLTGOT]->d_un.d_val);
Elf32_Word index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2;
Elf32_Word offset = index * 2 + PLT_INITIAL_ENTRY_WORDS;
if (index >= PLT_DOUBLE_SIZE)
{
/* Slots greater than or equal to 2^13 have 4 words
available instead of two. */
plt[offset ] = OPCODE_LI (11,finaladdr);
plt[offset+1] = OPCODE_ADDIS (11,11,finaladdr + 0x8000 >> 16);
plt[offset+2] = OPCODE_MTCTR (11);
plt[offset+3] = OPCODE_BCTR ();
}
else
{
Elf32_Word num_plt_entries;
Elf32_Word rel_offset_words;
num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
/ sizeof(Elf32_Rela));
rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
plt[offset ] = OPCODE_LI (11,index * 4);
plt[offset+1] = OPCODE_B (-(4 * (offset + 1
- PLT_LONGBRANCH_ENTRY_WORDS)));
plt[index + rel_offset_words] = finaladdr;
}
}
MODIFIED_CODE(reloc_addr);
}
else
assert (! "unexpected dynamic reloc type");
if (rinfo == R_PPC_ADDR16_LO ||
rinfo == R_PPC_ADDR16_HI ||
rinfo == R_PPC_ADDR16_HA ||
rinfo == R_PPC_REL24 ||
rinfo == R_PPC_ADDR24)
MODIFIED_CODE_NOQUEUE (reloc_addr);
}
#define ELF_MACHINE_NO_REL 1
#endif
/* Nonzero iff TYPE describes relocation of a PLT entry, so
PLT entries should not be allowed to define the value. */
#define elf_machine_pltrel_p(type) ((type) == R_PPC_JMP_SLOT)
/* Set up the loaded object described by L so its unrelocated PLT
entries will jump to the on-demand fixup code in dl-runtime.c. */
static inline void
elf_machine_runtime_setup (struct link_map *map, int lazy)
{
if (map->l_info[DT_JMPREL])
{
int i;
/* Fill in the PLT. Its initial contents are directed to a
function earlier in the PLT which arranges for the dynamic
linker to be called back. */
Elf32_Word *plt = (Elf32_Word *) ((char *) map->l_addr
+ map->l_info[DT_PLTGOT]->d_un.d_val);
Elf32_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
/ sizeof (Elf32_Rela));
Elf32_Word rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
extern void _dl_runtime_resolve (void);
Elf32_Word size_modified;
if (lazy)
for (i = 0; i < num_plt_entries; i++)
{
Elf32_Word offset = PLT_ENTRY_START_WORDS(i);
if (i >= PLT_DOUBLE_SIZE)
{
plt[offset ] = OPCODE_LI (11, i * 4);
plt[offset+1] = OPCODE_ADDIS (11, 11, i * 4 + 0x8000 >> 16);
plt[offset+2] = OPCODE_B (-(4 * (offset + 2)));
}
else
{
plt[offset ] = OPCODE_LI (11, i * 4);
plt[offset+1] = OPCODE_B(-(4 * (offset + 1)));
}
/* Multiply index of entry, by 0xC. */
plt[0] = OPCODE_SLWI (12, 11, 1);
plt[1] = OPCODE_ADD (11, 12, 11);
if ((Elf32_Word) (char *) _dl_runtime_resolve <= 0x01fffffc
|| (Elf32_Word) (char *) _dl_runtime_resolve >= 0xfe000000)
{
plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map);
plt[3] = OPCODE_ADDIS (12, 12,
(Elf32_Word) (char *) map + 0x8000 >> 16);
plt[4] = OPCODE_BA ((Elf32_Word) (char *) _dl_runtime_resolve);
}
}
else
{
plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) _dl_runtime_resolve);
plt[3] = OPCODE_ADDIS(12, 12, 0x8000 +
((Elf32_Word) (char *) _dl_runtime_resolve
>> 16));
plt[4] = OPCODE_MTCTR (12);
plt[5] = OPCODE_LI (12, (Elf32_Word) (char *) map);
plt[6] = OPCODE_ADDIS (12, 12, ((Elf32_Word) (char *) map
+ 0x8000 >> 16));
plt[7] = OPCODE_BCTR ();
}
plt[PLT_LONGBRANCH_ENTRY_WORDS] =
OPCODE_ADDIS (11, 11, (Elf32_Word) (char*) (plt + rel_offset_words)
+ 0x8000 >> 16);
plt[PLT_LONGBRANCH_ENTRY_WORDS+1] =
OPCODE_LWZ(11,(Elf32_Word)(char*)(plt+rel_offset_words),11);
plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR (11);
plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR ();
size_modified = lazy ? rel_offset_words : PLT_INITIAL_ENTRY_WORDS;
/* Now we need to keep the caches in sync. */
for (i = 0; i < size_modified; i+=8)
PPC_DCBST (plt + i);
PPC_SYNC;
for (i = 0; i < size_modified; i+=8)
PPC_ICBI (plt + i);
PPC_ISYNC;
}
}
static inline void
elf_machine_lazy_rel (struct link_map *map, const Elf32_Rela *reloc)
{
assert (ELF32_R_TYPE (reloc->r_info) == R_PPC_JMP_SLOT);
/* elf_machine_runtime_setup handles this. */
}
/* The PLT uses Elf32_Rela relocs. */
#define elf_machine_relplt elf_machine_rela
/* This code is used in dl-runtime.c to call the `fixup' function
and then redirect to the address it returns. It is called
from code built in the PLT by elf_machine_runtime_setup. */
#define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
.section \".text\"
.align 2
.globl _dl_runtime_resolve
.type _dl_runtime_resolve,@function
_dl_runtime_resolve:
# We need to save the registers used to pass parameters.
# We build a stack frame to put them in.
stwu 1,-48(1)
mflr 0
stw 3,16(1)
stw 4,20(1)
stw 0,52(1)
stw 5,24(1)
# We also need to save some of the condition register fields.
mfcr 0
stw 6,28(1)
stw 7,32(1)
stw 8,36(1)
stw 9,40(1)
stw 10,44(1)
stw 0,12(1)
# The code that calls this has put parameters for `fixup' in r12 and r11.
mr 3,12
mr 4,11
bl fixup
# 'fixup' returns the address we want to branch to.
mtctr 3
# Put the registers back...
lwz 0,52(1)
lwz 10,44(1)
lwz 9,40(1)
mtlr 0
lwz 0,12(1)
lwz 8,36(1)
lwz 7,32(1)
lwz 6,28(1)
mtcrf 0xFF,0
lwz 5,24(1)
lwz 4,20(1)
lwz 3,16(1)
# ...unwind the stack frame, and jump to the PLT entry we updated.
addi 1,1,48
bctr
0:
.size _dl_runtime_resolve,0b-_dl_runtime_resolve
# undo '.section text'.
.previous
");
/* Initial entry point code for the dynamic linker.
The C function `_dl_start' is the real entry point;
its return value is the user program's entry point. */
#define RTLD_START \
asm ("\
.text
.align 2
.globl _start
.type _start,@function
_start:
# We start with the following on the stack, from top:
# argc (4 bytes)
# arguments for program (terminated by NULL)
# environment variables (terminated by NULL)
# arguments for the program loader
# FIXME: perhaps this should do the same trick as elf/start.c?
# Call _dl_start with one parameter pointing at argc
mr 3,1
# (we have to frob the stack pointer a bit to allow room for
# _dl_start to save the link register)
li 4,0
addi 1,1,-16
stw 4,0(1)
bl _dl_start@local
# Now, we do our main work of calling initialisation procedures.
# The ELF ABI doesn't say anything about parameters for these,
# so we just pass argc, argv, and the environment.
# Changing these is strongly discouraged (not least because argc is
# passed by value!).
# put our GOT pointer in r31
bl _GLOBAL_OFFSET_TABLE_-4@local
mflr 31
# the address of _start in r30
mr 30,3
# &_dl_argc in 29, &_dl_argv in 27, and _dl_default_scope in 28
lwz 28,_dl_default_scope@got(31)
lwz 29,_dl_argc@got(31)
lwz 27,_dl_argv@got(31)
0:
# call initfunc = _dl_init_next(_dl_default_scope[2])
lwz 3,8(28)
bl _dl_init_next@plt
# if initfunc is NULL, we exit the loop
mr. 0,3
beq 1f
# call initfunc(_dl_argc, _dl_argv, _dl_argv+_dl_argc+1)
mtlr 0
lwz 3,0(29)
lwz 4,0(27)
slwi 5,3,2
add 5,4,5
addi 5,5,4
blrl
# and loop.
b 0b
1:
# Now, to conform to the ELF ABI, we have to:
# pass argv (actually _dl_argv) in r4
lwz 4,0(27)
# pass argc (actually _dl_argc) in r3
lwz 3,0(29)
# pass envp (actually _dl_argv+_dl_argc+1) in r5
slwi 5,3,2
add 5,4,5
addi 5,5,4
# pass the auxilary vector in r6. This is passed just after _envp.
addi 6,5,-4
2: lwzu 0,4(6)
cmpwi 1,0,0
bne 2b
addi 6,6,4
# pass a termination function pointer (in this case _dl_fini) in r7
lwz 7,_dl_fini@got(31)
# now, call the start function in r30...
mtctr 30
# pass the stack pointer in r1 (so far so good), pointing to a NULL value
# (this lets our startup code distinguish between a program linked statically,
# which linux will call with argc on top of the stack which will hopefully
# never be zero, and a dynamically linked program which will always have
# a NULL on the top of the stack).
# Take the opportunity to clear LR, so anyone who accidentally returns
# from _start gets SEGV.
li 0,0
stw 0,0(1)
mtlr 0
# and also clear _dl_starting_up
lwz 26,_dl_starting_up@got(31)
stw 0,0(26)
# go do it!
bctr
0:
.size _start,0b-_start
# undo '.section text'.
.previous
");
#define ELF_PREFERRED_ADDRESS_DATA \
static ElfW(Addr) _dl_preferred_address = 0;
#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) \
( { \
ElfW(Addr) prefd; \
if (mapstartpref != 0 && _dl_preferred_address == 0) \
_dl_preferred_address = mapstartpref; \
if (mapstartpref != 0) \
prefd = mapstartpref; \
else if (_dl_preferred_address < maplength + 0x50000) \
prefd = 0; \
else \
prefd = _dl_preferred_address = \
((_dl_preferred_address - maplength - 0x10000) \
& ~(_dl_pagesize - 1)); \
prefd; \
} )
#define ELF_FIXED_ADDRESS(loader, mapstart) \
( { \
if (mapstart != 0 && _dl_preferred_address < mapstart) \
_dl_preferred_address = mapstart; \
} )
#define ELF_FIXUP_RETURNS_ADDRESS 1