[PATCH 11/12] metag: Add NPTL support

Markos Chandras markos.chandras at gmail.com
Thu Feb 21 10:12:08 UTC 2013


From: Markos Chandras <markos.chandras at imgtec.com>

Signed-off-by: Markos Chandras <markos.chandras at imgtec.com>
---
 include/elf.h                                      |   5 +
 ldso/ldso/metag/dl-debug.h                         |   8 +-
 ldso/ldso/metag/dl-startup.h                       |   5 +
 ldso/ldso/metag/dl-sysdep.h                        |  19 +-
 ldso/ldso/metag/elfinterp.c                        | 119 +++++----
 ldso/ldso/metag/metag_load_tp.S                    |  50 ++++
 libc/sysdeps/linux/common/Makefile.in              |   1 +
 libc/sysdeps/linux/metag/Makefile.arch             |   4 +-
 libc/sysdeps/linux/metag/bits/atomic.h             |  77 ++++++
 libc/sysdeps/linux/metag/bits/syscalls.h           |  73 ++++--
 libc/sysdeps/linux/metag/clone.S                   |  50 +++-
 libc/sysdeps/linux/metag/crt1.S                    |   3 +-
 libc/sysdeps/linux/metag/libc-metag_load_tp.S      |   1 +
 libc/sysdeps/linux/metag/sysdep.h                  |  71 ++++++
 libc/sysdeps/linux/metag/vfork.S                   |  80 ++++++
 libpthread/nptl/sysdeps/metag/Makefile.arch        |  17 ++
 libpthread/nptl/sysdeps/metag/dl-tls.h             |  29 +++
 libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h      |  36 +++
 libpthread/nptl/sysdeps/metag/libc-tls.c           |  31 +++
 libpthread/nptl/sysdeps/metag/metag_load_tp.S      |   1 +
 libpthread/nptl/sysdeps/metag/pthread_spin_init.c  |  20 ++
 libpthread/nptl/sysdeps/metag/pthread_spin_lock.S  |  33 +++
 .../nptl/sysdeps/metag/pthread_spin_trylock.S      |  37 +++
 .../nptl/sysdeps/metag/pthread_spin_unlock.S       |  29 +++
 libpthread/nptl/sysdeps/metag/pthreaddef.h         |  40 +++
 libpthread/nptl/sysdeps/metag/tcb-offsets.sym      |  15 ++
 libpthread/nptl/sysdeps/metag/tls.h                | 163 ++++++++++++
 .../nptl/sysdeps/unix/sysv/linux/metag/Makefile    |  13 +
 .../sysdeps/unix/sysv/linux/metag/Makefile.arch    |  34 +++
 .../unix/sysv/linux/metag/bits/pthreadtypes.h      | 181 +++++++++++++
 .../sysdeps/unix/sysv/linux/metag/bits/semaphore.h |  35 +++
 .../nptl/sysdeps/unix/sysv/linux/metag/clone.S     |   3 +
 .../sysdeps/unix/sysv/linux/metag/createthread.c   |  23 ++
 .../nptl/sysdeps/unix/sysv/linux/metag/fork.c      |  31 +++
 .../unix/sysv/linux/metag/libc-lowlevellock.c      |  21 ++
 .../sysdeps/unix/sysv/linux/metag/lowlevellock.c   | 134 ++++++++++
 .../sysdeps/unix/sysv/linux/metag/lowlevellock.h   | 282 +++++++++++++++++++++
 .../unix/sysv/linux/metag/pt-__syscall_error.c     |   1 +
 .../sysv/linux/metag/pt-__syscall_rt_sigaction.c   |   1 +
 .../unix/sysv/linux/metag/pt-gettimeofday.c        |   5 +
 .../nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S  |  51 ++++
 .../sysdeps/unix/sysv/linux/metag/pthread_once.c   | 100 ++++++++
 .../sysdeps/unix/sysv/linux/metag/sysdep-cancel.h  | 152 +++++++++++
 .../nptl/sysdeps/unix/sysv/linux/metag/vfork.S     |  56 ++++
 44 files changed, 2045 insertions(+), 95 deletions(-)
 create mode 100644 ldso/ldso/metag/metag_load_tp.S
 create mode 100644 libc/sysdeps/linux/metag/bits/atomic.h
 create mode 100644 libc/sysdeps/linux/metag/libc-metag_load_tp.S
 create mode 100644 libc/sysdeps/linux/metag/sysdep.h
 create mode 100644 libc/sysdeps/linux/metag/vfork.S
 create mode 100644 libpthread/nptl/sysdeps/metag/Makefile.arch
 create mode 100644 libpthread/nptl/sysdeps/metag/dl-tls.h
 create mode 100644 libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h
 create mode 100644 libpthread/nptl/sysdeps/metag/libc-tls.c
 create mode 100644 libpthread/nptl/sysdeps/metag/metag_load_tp.S
 create mode 100644 libpthread/nptl/sysdeps/metag/pthread_spin_init.c
 create mode 100644 libpthread/nptl/sysdeps/metag/pthread_spin_lock.S
 create mode 100644 libpthread/nptl/sysdeps/metag/pthread_spin_trylock.S
 create mode 100644 libpthread/nptl/sysdeps/metag/pthread_spin_unlock.S
 create mode 100644 libpthread/nptl/sysdeps/metag/pthreaddef.h
 create mode 100644 libpthread/nptl/sysdeps/metag/tcb-offsets.sym
 create mode 100644 libpthread/nptl/sysdeps/metag/tls.h
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile.arch
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/pthreadtypes.h
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/semaphore.h
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/clone.S
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/createthread.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/fork.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/libc-lowlevellock.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.h
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_error.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_rt_sigaction.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-gettimeofday.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/pthread_once.c
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/sysdep-cancel.h
 create mode 100644 libpthread/nptl/sysdeps/unix/sysv/linux/metag/vfork.S

diff --git a/include/elf.h b/include/elf.h
index 877d081..cbb4da2 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -3184,6 +3184,11 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_METAG_RELATIVE                45
 #define R_METAG_GLOB_DAT                46
 
+/* TLS relocations */
+#define R_METAG_TLS_TPOFF               56
+#define R_METAG_TLS_DTPMOD              57
+#define R_METAG_TLS_DTPOFF              58
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/ldso/ldso/metag/dl-debug.h b/ldso/ldso/metag/dl-debug.h
index 45f7072..d293ec8 100644
--- a/ldso/ldso/metag/dl-debug.h
+++ b/ldso/ldso/metag/dl-debug.h
@@ -37,11 +37,17 @@ static const char *_dl_reltypes_tab[] = {
 	[6]  "R_METAG_REG32OP1", "R_METAG_REG32OP2", "R_METAG_REG32OP3",
 	[9]  "R_METAG_REG16OP1", "R_METAG_REG16OP2", "R_METAG_REG16OP3",
 	[12] "R_METAG_REG32OP4", "R_METAG_HIOG", "R_METAG_LOOG",
-	[30] "R_METAG_VTINHERIT", "R_METAG_VTENTRY",
+	[30] "R_METAG_GNU_VTINHERIT", "R_METAG_GNU_VTENTRY",
 	[32] "R_METAG_HI16_GOTOFF", "R_METAG_LO16_GOTOFF",
 	[34] "R_METAG_GETSET_GOTOFF", "R_METAG_GETSET_GOT",
 	[36] "R_METAG_HI16_GOTPC", "R_METAG_LO16_GOTPC",
 	[38] "R_METAG_HI16_PLT", "R_METAG_LO16_PLT",
 	[40] "R_METAG_RELBRANCH_PLT", "R_METAG_GOTOFF",
 	[42] "R_METAG_PLT", "R_METAG_COPY", "R_METAG_JMP_SLOT",
+	[45] "R_METAG_RELATIVE", "R_METAG_GLOB_DAT", "R_METAG_TLS_GD",
+	[48] "R_METAG_TLS_LDM", "R_METAG_TLS_LDO_HI16", "R_METAG_TLS_LDO_LO16",
+	[51] "R_METAG_TLS_LDO", "R_METAG_TLS_IE", "R_METAG_TLS_IENONPIC",
+	[54] "R_METAG_TLS_IENONPIC_HI16", "R_METAG_TLS_IENONPIC_LO16",
+	[56] "R_METAG_TLS_TPOFF", "R_METAG_TLS_DTPMOD", "R_METAG_TLS_DTPOFF",
+	[59] "R_METAG_TLS_LE", "R_METAG_TLS_LE_HI16", "R_METAG_TLS_LE_LO16"
 };
diff --git a/ldso/ldso/metag/dl-startup.h b/ldso/ldso/metag/dl-startup.h
index f132553..3269316 100644
--- a/ldso/ldso/metag/dl-startup.h
+++ b/ldso/ldso/metag/dl-startup.h
@@ -22,6 +22,11 @@ __asm__ (
 "       CALLR   D1RtP,__dl_start\n"
 "       GETL    D0Ar2,D1Ar1,[A0StP+#-(1*8)]\n"
 "       GETL    D0Ar4,D1Ar3,[A0StP+#-(2*8)]\n"
+"       ADDT    A1LbP,CPC1,#HI(__GLOBAL_OFFSET_TABLE__)\n"
+"       ADD     A1LbP,A1LbP,#LO(__GLOBAL_OFFSET_TABLE__+4)\n"
+"       ADDT    A1LbP,A1LbP,#HI(__dl_fini at GOTOFF)\n"
+"       ADD     A1LbP,A1LbP,#LO(__dl_fini at GOTOFF)\n"
+"       MOV     D0Ar4, A1LbP\n"
 "       SUB     A0StP,A0StP,#(2*8)\n"
 "       MOV     PC,D0Re0\n"
 "       .size __start,.-__start\n"
diff --git a/ldso/ldso/metag/dl-sysdep.h b/ldso/ldso/metag/dl-sysdep.h
index 48059a3..a9b6c05 100644
--- a/ldso/ldso/metag/dl-sysdep.h
+++ b/ldso/ldso/metag/dl-sysdep.h
@@ -56,14 +56,17 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entr
 #define PAGE_ALIGN (~ADDR_ALIGN)
 #define OFFS_ALIGN (PAGE_ALIGN & ~(1ul << (sizeof(_dl_pagesize) * 8 - 1)))
 
-/* The union of reloc-type-classes where the reloc TYPE is a member.
-
-   TYPE is in the class ELF_RTYPE_CLASS_NOCOPY if it should not be allowed
-   to resolve to one of the main executable's symbols, as for a COPY
-   reloc.  */
-#define elf_machine_type_class(type)				\
-  (((((type) == R_METAG_JMP_SLOT)) * ELF_RTYPE_CLASS_PLT)	\
-   | (((type) == R_METAG_COPY) * ELF_RTYPE_CLASS_COPY))
+/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
+   TLS variable, so undefined references should not be allowed to
+   define the value.
+
+   ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
+   of the main executable's symbols, as for a COPY reloc.  */
+#define elf_machine_type_class(type)                                 \
+  ((((type) == R_METAG_JMP_SLOT || (type) == R_METAG_TLS_DTPMOD      \
+     || (type) == R_METAG_TLS_DTPOFF || (type) == R_METAG_TLS_TPOFF) \
+     * ELF_RTYPE_CLASS_PLT)                                          \
+     | (((type) == R_METAG_COPY) * ELF_RTYPE_CLASS_COPY))
 
 static inline Elf32_Addr
 elf_machine_dynamic(Elf32_Ehdr *header)
diff --git a/ldso/ldso/metag/elfinterp.c b/ldso/ldso/metag/elfinterp.c
index e076303..e33dba0 100644
--- a/ldso/ldso/metag/elfinterp.c
+++ b/ldso/ldso/metag/elfinterp.c
@@ -63,43 +63,36 @@ static inline void __put_unaligned_reloc(unsigned long *addr,
 unsigned long
 _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 {
-	int reloc_type;
 	int symtab_index;
 	char *strtab;
 	char *symname;
 	char *new_addr;
 	char *rel_addr;
 	char **got_addr;
-	Elf32_Sym *symtab;
+	ElfW(Sym) *symtab;
 	ELF_RELOC *this_reloc;
 	unsigned long instr_addr;
 
 	rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
 
 	this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
-	reloc_type = ELF32_R_TYPE(this_reloc->r_info);
-	symtab_index = ELF32_R_SYM(this_reloc->r_info);
+	symtab_index = ELF_R_SYM(this_reloc->r_info);
 
-	symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
+	symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
 	strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
 	symname = strtab + symtab[symtab_index].st_name;
 
-	if (unlikely(reloc_type != R_METAG_JMP_SLOT)) {
-		_dl_dprintf(2, "%s: Incorrect relocation type in jump relocations\n",
-			    _dl_progname);
-		_dl_exit(1);
-	}
-
 	/* Address of the jump instruction to fix up. */
 	instr_addr = ((unsigned long)this_reloc->r_offset +
 		      (unsigned long)tpnt->loadaddr);
 	got_addr = (char **)instr_addr;
 
 	/* Get the address of the GOT entry. */
-	new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt,
+	new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt,
 			ELF_RTYPE_CLASS_PLT, NULL);
 	if (unlikely(!new_addr)) {
-		_dl_dprintf(2, "%s: Can't resolve symbol '%s'\n", _dl_progname, symname);
+		_dl_dprintf(2, "%s: Can't resolve symbol '%s'\n",
+			    _dl_progname, symname);
 		_dl_exit(1);
 	}
 
@@ -122,28 +115,28 @@ _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 }
 
 static int
-_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
+_dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	  unsigned long rel_addr, unsigned long rel_size,
-	  int (*reloc_fnc)(struct elf_resolve *tpnt, struct dyn_elf *scope,
-			   ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
+	  int (*reloc_fnc)(struct elf_resolve *tpnt, struct r_scope_elem *scope,
+			   ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
 {
 	int symtab_index;
 	unsigned int i;
 	char *strtab;
-	Elf32_Sym *symtab;
+	ElfW(Sym) *symtab;
 	ELF_RELOC *rpnt;
 
 	/* Parse the relocation information. */
 	rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
 	rel_size /= sizeof(ELF_RELOC);
 
-	symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
+	symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
 	strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
 
 	for (i = 0; i < rel_size; i++, rpnt++) {
 		int res;
 
-		symtab_index = ELF32_R_SYM(rpnt->r_info);
+		symtab_index = ELF_R_SYM(rpnt->r_info);
 
 		debug_sym(symtab, strtab, symtab_index);
 		debug_reloc(symtab, strtab, rpnt);
@@ -161,7 +154,7 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
 				    strtab + symtab[symtab_index].st_name);
 
 		if (unlikely(res < 0)) {
-			int reloc_type = ELF32_R_TYPE(rpnt->r_info);
+			int reloc_type = ELF_R_TYPE(rpnt->r_info);
 
 #if defined (__SUPPORT_LD_DEBUG__)
 			_dl_dprintf(2, "can't handle reloc type %s\n",
@@ -181,8 +174,8 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
 }
 
 static int
-_dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
-	     ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
+_dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
+	     ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
 {
 	int reloc_type;
 	int symtab_index;
@@ -190,30 +183,35 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 	unsigned long *reloc_addr;
 	unsigned long symbol_addr;
 #if defined (__SUPPORT_LD_DEBUG__)
-	unsigned long old_val;
+	unsigned long old_val = 0;
 #endif
+	struct elf_resolve *tls_tpnt = NULL;
+	struct symbol_ref sym_ref;
 
-	reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
-	reloc_type = ELF32_R_TYPE(rpnt->r_info);
-	symtab_index = ELF32_R_SYM(rpnt->r_info);
+	reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset);
+	reloc_type = ELF_R_TYPE(rpnt->r_info);
+	symtab_index = ELF_R_SYM(rpnt->r_info);
 	symbol_addr = 0;
-	symname = strtab + symtab[symtab_index].st_name;
+	sym_ref.sym = &symtab[symtab_index];
+	sym_ref.tpnt = NULL;
 
 	if (symtab_index) {
-		if (symtab[symtab_index].st_shndx != SHN_UNDEF &&
-		    ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL) {
-			symbol_addr = (unsigned long)tpnt->loadaddr;
-		} else {
-		  symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
-							     elf_machine_type_class(reloc_type), NULL);
-		}
-
-		if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
-			_dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
-			_dl_exit(1);
+		symname = strtab + symtab[symtab_index].st_name;
+		symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
+							   elf_machine_type_class(reloc_type), &sym_ref);
+
+		if (!symbol_addr
+		    && ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS
+		    && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) {
+			_dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
+				    _dl_progname, symname);
+			return 1;
 		};
-
-		symbol_addr += rpnt->r_addend;
+		if (_dl_trace_prelink) {
+			_dl_debug_lookup(symname, tpnt, &symtab[symtab_index],
+				&sym_ref, elf_machine_type_class(reloc_type));
+		}
+		tls_tpnt = sym_ref.tpnt;
 	}
 
 #if defined (__SUPPORT_LD_DEBUG__)
@@ -221,13 +219,21 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 		old_val = __get_unaligned_reloc(reloc_addr);
 #endif
 
+#if defined USE_TLS && USE_TLS
+	/* In case of a TLS reloc, tls_tpnt NULL means we have an 'anonymous'
+	   symbol.  This is the case for a static tls variable, so the lookup
+	   module is just that one is referencing the tls variable. */
+	if (!tls_tpnt)
+		tls_tpnt = tpnt;
+#endif
 	switch (reloc_type) {
 		case R_METAG_NONE:
 			break;
 		case R_METAG_GLOB_DAT:
 		case R_METAG_JMP_SLOT:
 		case R_METAG_ADDR32:
-			__put_unaligned_reloc(reloc_addr, symbol_addr);
+			__put_unaligned_reloc(reloc_addr,
+					      symbol_addr + rpnt->r_addend);
 			break;
 		case R_METAG_COPY:
 #if defined (__SUPPORT_LD_DEBUG__)
@@ -235,11 +241,12 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 				_dl_dprintf(_dl_debug_file,
 					    "\t%s move %d bytes from %x to %x\n",
 					    symname, symtab[symtab_index].st_size,
-					    symbol_addr, reloc_addr);
+					    symbol_addr + rpnt->r_addend,
+					    reloc_addr);
 #endif
 
 			_dl_memcpy((char *)reloc_addr,
-				   (char *)symbol_addr,
+				   (char *)symbol_addr + rpnt->r_addend,
 				   symtab[symtab_index].st_size);
 			break;
 		case R_METAG_RELATIVE:
@@ -247,13 +254,24 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 					      (unsigned long)tpnt->loadaddr +
 					      rpnt->r_addend);
 			break;
+#if defined USE_TLS && USE_TLS
+		case R_METAG_TLS_DTPMOD:
+			*reloc_addr = tls_tpnt->l_tls_modid;
+			break;
+		case R_METAG_TLS_DTPOFF:
+			*reloc_addr = symbol_addr;
+			break;
+		case R_METAG_TLS_TPOFF:
+			CHECK_STATIC_TLS ((struct link_map *) tls_tpnt);
+			*reloc_addr = tls_tpnt->l_tls_offset + symbol_addr + rpnt->r_addend;
+			break;
+#endif
 		default:
 			return -1;	/* Calls _dl_exit(1). */
 	}
 
 #if defined (__SUPPORT_LD_DEBUG__)
-	if (_dl_debug_reloc && _dl_debug_detail &&
-	    (reloc_type != R_METAG_NONE)) {
+	if (_dl_debug_reloc && _dl_debug_detail && reloc_type != R_METAG_NONE) {
 		unsigned long new_val = __get_unaligned_reloc(reloc_addr);
 		_dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
 			    old_val, new_val, reloc_addr);
@@ -264,8 +282,8 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 }
 
 static int
-_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
-		  ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
+_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
+		  ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
 {
 	int reloc_type;
 	unsigned long *reloc_addr;
@@ -273,8 +291,8 @@ _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
 	unsigned long old_val;
 #endif
 
-	reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
-	reloc_type = ELF32_R_TYPE(rpnt->r_info);
+	reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset);
+	reloc_type = ELF_R_TYPE(rpnt->r_info);
 
 #if defined (__SUPPORT_LD_DEBUG__)
 	old_val = *reloc_addr;
@@ -311,9 +329,10 @@ _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
 
 int
 _dl_parse_relocation_information(struct dyn_elf *rpnt,
+				 struct r_scope_elem *scope,
 				 unsigned long rel_addr,
 				 unsigned long rel_size)
 {
-	return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr,
+	return _dl_parse(rpnt->dyn, scope, rel_addr,
 			 rel_size, _dl_do_reloc);
 }
diff --git a/ldso/ldso/metag/metag_load_tp.S b/ldso/ldso/metag/metag_load_tp.S
new file mode 100644
index 0000000..1179712
--- /dev/null
+++ b/ldso/ldso/metag/metag_load_tp.S
@@ -0,0 +1,50 @@
+/* Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file. (The GNU Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   Note that people who make modified versions of this file are not
+   obligated to grant this special exception for their modified
+   versions; it is their choice whether to do so. The GNU Lesser
+   General Public License gives permission to release a modified
+   version without this exception; this exception also makes it
+   possible to release a modified version which carries forward this
+   exception.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <features.h>
+
+#ifdef __UCLIBC_HAS_THREADS_NATIVE__
+
+#include <sysdep.h>
+
+	.text
+	.global ___metag_load_tp
+	.type   ___metag_load_tp, at function
+
+___metag_load_tp:
+	MOVT	D1Ar1,#HI(0x6ffff000)
+	JUMP	D1Ar1,#LO(0x6ffff000)
+	.size   ___metag_load_tp,.-___metag_load_tp
+
+#endif /* __UCLIBC_HAS_THREADS_NATIVE__ */
diff --git a/libc/sysdeps/linux/common/Makefile.in b/libc/sysdeps/linux/common/Makefile.in
index f7083a4..dbf0b0f 100644
--- a/libc/sysdeps/linux/common/Makefile.in
+++ b/libc/sysdeps/linux/common/Makefile.in
@@ -69,6 +69,7 @@ CSRC- += fork.c getpid.c raise.c #open.c close.c read.c write.c
 CSRC- += $(if $(findstring =arm=,=$(TARGET_ARCH)=),vfork.c)
 CSRC- += $(if $(findstring =x86_64=,=$(TARGET_ARCH)=),vfork.c)
 #CSRC- += $(if $(findstring =mips=y=,=$(TARGET_ARCH)=$(CONFIG_MIPS_O32_ABI)=),waitpid.c)
+CSRC- += $(if $(findstring =metag=,=$(TARGET_ARCH)=),vfork.c)
 endif
 ifneq ($(ARCH_HAS_DEPRECATED_SYSCALLS),y)
 # No conversion is needed for new architectures
diff --git a/libc/sysdeps/linux/metag/Makefile.arch b/libc/sysdeps/linux/metag/Makefile.arch
index 0ca6f24..7619e83 100644
--- a/libc/sysdeps/linux/metag/Makefile.arch
+++ b/libc/sysdeps/linux/metag/Makefile.arch
@@ -7,4 +7,6 @@
 
 CSRC-y := brk.c syscall.c metag.c __syscall_error.c
 
-SSRC-y := _longjmp.S clone.S setjmp.S
+SSRC-y := _longjmp.S clone.S setjmp.S vfork.S
+
+SSRC-$(UCLIBC_HAS_THREADS_NATIVE) += libc-metag_load_tp.S
diff --git a/libc/sysdeps/linux/metag/bits/atomic.h b/libc/sysdeps/linux/metag/bits/atomic.h
new file mode 100644
index 0000000..2c5f2f7
--- /dev/null
+++ b/libc/sysdeps/linux/metag/bits/atomic.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002, 2003, 2004, 2005 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdint.h>
+#include <sysdep.h>
+
+typedef int8_t atomic8_t;
+typedef uint8_t uatomic8_t;
+typedef int_fast8_t atomic_fast8_t;
+typedef uint_fast8_t uatomic_fast8_t;
+
+typedef int32_t atomic32_t;
+typedef uint32_t uatomic32_t;
+typedef int_fast32_t atomic_fast32_t;
+typedef uint_fast32_t uatomic_fast32_t;
+
+typedef intptr_t atomicptr_t;
+typedef uintptr_t uatomicptr_t;
+typedef intmax_t atomic_max_t;
+typedef uintmax_t uatomic_max_t;
+
+void __metag_link_error (void);
+
+#define atomic_full_barrier() \
+     __asm__ __volatile__("": : :"memory")
+
+/* Atomic compare and exchange.  This sequence relies on the kernel to
+   provide a compare and exchange operation which is atomic. */
+
+#define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
+  ({ __metag_link_error (); oldval; })
+
+#define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
+  ({ __metag_link_error (); oldval; })
+
+/* This code uses the kernel helper to do cmpxchg. It relies on the fact
+   the helper code only clobbers D0Re0. */
+#define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval)     \
+  ({ register __typeof (oldval) a_current __asm__ ("D1Ar1");		\
+    register __typeof (oldval) a_newval __asm__ ("D0Ar2") = (newval);   \
+    register __typeof (mem) a_ptr __asm__ ("D1Ar3") = (mem);            \
+    register __typeof (oldval) a_oldval __asm__ ("D0Ar4") = (oldval);	\
+    __asm__ __volatile__						\
+      ("0:\n\t"								\
+       "GETD	%[cur], [%[ptr]]\n\t"					\
+       "CMP	%[cur], %[old]\n\t"					\
+       "BNE	1f\n\t"							\
+       "MOVT	D1RtP, #0x6fff\n\t"					\
+       "ADD	D1RtP, D1RtP, #0xf040\n\t"				\
+       "SWAP	D1RtP, PC\n\t"						\
+       "MOV	%[cur], %[old]\n\t"					\
+       "CMP	D0Re0, #0\n\t"						\
+       "BNE	0b\n\t"							\
+       "1:"								\
+       : [cur] "=&r" (a_current)					\
+       : [new] "r" (a_newval), [ptr] "r" (a_ptr),			\
+	 [old] "r" (a_oldval)						\
+       : "D0Re0", "D1RtP", "cc", "memory");				\
+    a_current; })
+
+#define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
+  ({ __metag_link_error (); oldval; })
diff --git a/libc/sysdeps/linux/metag/bits/syscalls.h b/libc/sysdeps/linux/metag/bits/syscalls.h
index f0ee81d..a199b43 100644
--- a/libc/sysdeps/linux/metag/bits/syscalls.h
+++ b/libc/sysdeps/linux/metag/bits/syscalls.h
@@ -39,8 +39,9 @@
 #define INTERNAL_SYSCALL_NCS(name, err, nr, args...)			\
   ({unsigned int __sys_result;						\
     {									\
+      PREP_ARGS_##nr (args);						\
       register int _result __asm__ ("D0Re0"), _nr __asm__ ("D1Re0");	\
-      LOAD_ARGS_##nr (args);						\
+      LOAD_ARGS_##nr;							\
       _nr = (name);							\
       __asm__ volatile ("SWITCH  #0x440001 ! syscall " #name		\
 			: "=r" (_result)				\
@@ -57,32 +58,52 @@
 #undef INTERNAL_SYSCALL_ERRNO
 #define INTERNAL_SYSCALL_ERRNO(val, err)	(-(val))
 
-#define LOAD_ARGS_0()
+#define PREP_ARGS_0()
+#define PREP_ARGS_1(a1)                                 \
+  int _t1 = (int) (a1);                                 \
+  PREP_ARGS_0 ()
+#define PREP_ARGS_2(a1, a2)                             \
+  int _t2 = (int) (a2);                                 \
+  PREP_ARGS_1 (a1)
+#define PREP_ARGS_3(a1, a2, a3)                         \
+  int _t3 = (int) (a3);                                 \
+  PREP_ARGS_2 (a1, a2)
+#define PREP_ARGS_4(a1, a2, a3, a4)                     \
+  int _t4 = (int) (a4);                                 \
+  PREP_ARGS_3 (a1, a2, a3)
+#define PREP_ARGS_5(a1, a2, a3, a4, a5)                 \
+  int _t5 = (int) (a5);                                 \
+  PREP_ARGS_4 (a1, a2, a3, a4)
+#define PREP_ARGS_6(a1, a2, a3, a4, a5, a6)             \
+  int _t6 = (int) (a6);                                 \
+  PREP_ARGS_5 (a1, a2, a3, a4, a5)
+
+#define LOAD_ARGS_0
 #define ASM_ARGS_0
-#define LOAD_ARGS_1(a1)					\
-  register int _a1 __asm__ ("D1Ar1") = (int) (a1);	\
-  LOAD_ARGS_0 ()
-#define ASM_ARGS_1	ASM_ARGS_0, "d" (_a1)
-#define LOAD_ARGS_2(a1, a2)				\
-  register int _a2 __asm__ ("D0Ar2") = (int) (a2);	\
-  LOAD_ARGS_1 (a1)
-#define ASM_ARGS_2	ASM_ARGS_1, "d" (_a2)
-#define LOAD_ARGS_3(a1, a2, a3)				\
-  register int _a3 __asm__ ("D1Ar3") = (int) (a3);	\
-  LOAD_ARGS_2 (a1, a2)
-#define ASM_ARGS_3	ASM_ARGS_2, "d" (_a3)
-#define LOAD_ARGS_4(a1, a2, a3, a4)			\
-  register int _a4 __asm__ ("D0Ar4") = (int) (a4);	\
-  LOAD_ARGS_3 (a1, a2, a3)
-#define ASM_ARGS_4	ASM_ARGS_3, "d" (_a4)
-#define LOAD_ARGS_5(a1, a2, a3, a4, a5)			\
-  register int _a5 __asm__ ("D1Ar5") = (int) (a5);	\
-  LOAD_ARGS_4 (a1, a2, a3, a4)
-#define ASM_ARGS_5	ASM_ARGS_4, "d" (_a5)
-#define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6)		\
-  register int _a6 __asm__ ("D0Ar6") = (int) (a6);	\
-  LOAD_ARGS_5 (a1, a2, a3, a4, a5)
-#define ASM_ARGS_6	ASM_ARGS_5, "d" (_a6)
+#define LOAD_ARGS_1                                     \
+  register int _a1 __asm__ ("D1Ar1") = (int) (_t1);     \
+  LOAD_ARGS_0
+#define ASM_ARGS_1 ASM_ARGS_0, "d" (_a1)
+#define LOAD_ARGS_2                                     \
+  register int _a2 __asm__ ("D0Ar2") = (int) (_t2);     \
+  LOAD_ARGS_1
+#define ASM_ARGS_2 ASM_ARGS_1, "d" (_a2)
+#define LOAD_ARGS_3                                     \
+  register int _a3 __asm__ ("D1Ar3") = (int) (_t3);     \
+  LOAD_ARGS_2
+#define ASM_ARGS_3 ASM_ARGS_2, "d" (_a3)
+#define LOAD_ARGS_4                                     \
+  register int _a4 __asm__ ("D0Ar4") = (int) (_t4);     \
+  LOAD_ARGS_3
+#define ASM_ARGS_4 ASM_ARGS_3, "d" (_a4)
+#define LOAD_ARGS_5                                     \
+  register int _a5 __asm__ ("D1Ar5") = (int) (_t5);     \
+  LOAD_ARGS_4
+#define ASM_ARGS_5 ASM_ARGS_4, "d" (_a5)
+#define LOAD_ARGS_6                                     \
+  register int _a6 __asm__ ("D0Ar6") = (int) (_t6);     \
+  LOAD_ARGS_5
+#define ASM_ARGS_6 ASM_ARGS_5, "d" (_a6)
 
 #endif /* __ASSEMBLER__ */
 #endif /* _BITS_SYSCALLS_H */
diff --git a/libc/sysdeps/linux/metag/clone.S b/libc/sysdeps/linux/metag/clone.S
index 2b85770..16b1252 100644
--- a/libc/sysdeps/linux/metag/clone.S
+++ b/libc/sysdeps/linux/metag/clone.S
@@ -6,7 +6,17 @@
 #include <asm/errno.h>
 #include <asm/unistd.h>
 
-/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg); */
+#define CLONE_VM      0x00000100
+#define CLONE_THREAD  0x00010000
+
+#ifdef __PIC__
+#define __CLONE_METAG_LOAD_TP ___metag_load_tp at PLT
+#else
+#define __CLONE_METAG_LOAD_TP ___metag_load_tp
+#endif
+
+/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg,
+			pid_t *ptid, struct user_desc *tls, pid_t *ctid); */
 
 	.text
 	.global	__clone
@@ -23,8 +33,12 @@ __clone:
 	MOV D0FrT, D1Ar1
 
 	! do the system call
-	! get flags
 	MOV	D1Ar1, D1Ar3
+	MOV	D1Ar3, D1Ar5
+	MOV	D1Ar5, D0Ar6
+	MOV	D0Ar6, D0Ar4
+	GETD	D0Ar4, [A0StP+#-4]
+
 	! new sp is already in D0Ar2
 	MOV	D1Re0, #__NR_clone
 	SWITCH	#0x440001
@@ -36,14 +50,36 @@ __clone:
 	! BRKPNT
 
 	! We are the child
-	! pick the function arg and call address off the stack and execute
-	MOV     D0Ar2, D0FrT
-	MOV     D1Ar1, D0Ar4
-	MOV     D1RtP, PC
+#ifdef RESET_PID
+	SETL	[A0StP++], D0FrT, D1RtP
+	MOVT	D0FrT, #HI(CLONE_THREAD)
+	ADD	D0FrT, D0FrT, #LO(CLONE_THREAD)
+	ANDS	D0FrT, D0FrT, D1Ar1
+	BNZ	3f
+	MOVT	D0FrT, #HI(CLONE_VM)
+	ADD	D0FrT, D0FrT, #LO(CLONE_VM)
+	ANDS	D0FrT, D0FrT, D1Ar1
+	BZ	1f
+	MOV	D1Ar1, #-1
+	BA	2f
+1:	MOV	D1Re0, #__NR_getpid
+	SWITCH	#0x440001
+	MOV	D1Ar1, D0Re0
+2:	CALLR	D1RtP, __CLONE_METAG_LOAD_TP
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE
+	SETD	[D0Re0 + #PID], D1Ar1
+	SETD	[D0Re0 + #TID], D1Ar1
+3:	GETL	D0FrT, D1RtP, [--A0StP]
+#endif
+	! Rearrange the function arg and call address from registers
+	MOV	D0Ar2, D0FrT
+	MOV	D1Ar1, D0Ar6
+	MOV	D1RtP, PC
 	ADD	D1RtP, D1RtP, #8
 	MOV	PC, D0Ar2
 
-	! and we are done, passing the return value through D0Re0
+	! and we are done, passing the return value D0Re0 through D1Ar1
+	MOV	D1Ar1, D0Re0
 #ifdef __PIC__
 	B	_exit at PLT
 #else
diff --git a/libc/sysdeps/linux/metag/crt1.S b/libc/sysdeps/linux/metag/crt1.S
index 6ffd8ed..ea52ce1 100644
--- a/libc/sysdeps/linux/metag/crt1.S
+++ b/libc/sysdeps/linux/metag/crt1.S
@@ -26,6 +26,7 @@ __start:
 
 	MOV	D1Ar3,D0Ar2			! argv
 	MOV	D0Ar2,D1Ar1			! argc
+	MOV	D0Ar6,D0Ar4			! rtld_fini
 
 #ifdef __PIC__
 	ADDT	A1LbP,CPC1,#HI(__GLOBAL_OFFSET_TABLE__)
@@ -51,8 +52,6 @@ __start:
 	ADD	D1Ar5,D1Ar5,#LO(__fini)
 #endif
 
-	MOV	D0Ar6,#0			! rtld_fini
-
 	MOVT	D1Re0,#0x80
 
 	SETL	[A0StP++],D0Re0,D1Re0		! stack_end (8Mb)
diff --git a/libc/sysdeps/linux/metag/libc-metag_load_tp.S b/libc/sysdeps/linux/metag/libc-metag_load_tp.S
new file mode 100644
index 0000000..8797b78
--- /dev/null
+++ b/libc/sysdeps/linux/metag/libc-metag_load_tp.S
@@ -0,0 +1 @@
+#include <ldso/ldso/metag/metag_load_tp.S>
diff --git a/libc/sysdeps/linux/metag/sysdep.h b/libc/sysdeps/linux/metag/sysdep.h
new file mode 100644
index 0000000..8045aa9
--- /dev/null
+++ b/libc/sysdeps/linux/metag/sysdep.h
@@ -0,0 +1,71 @@
+/* Assembler macros for META.
+   Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <common/sysdep.h>
+
+#include <features.h>
+#include <libc-internal.h>
+
+#ifdef	__ASSEMBLER__
+
+#ifdef SHARED
+#define PLTJMP(_x)	_x##@PLT
+#else
+#define PLTJMP(_x)	_x
+#endif
+
+#undef	PSEUDO_END
+#define	PSEUDO_END(name)						\
+  SYSCALL_ERROR_HANDLER							\
+  END (name)
+
+#if defined NOT_IN_libc
+# define SYSCALL_ERROR __local_syscall_error
+# ifdef RTLD_PRIVATE_ERRNO
+#  define SYSCALL_ERROR_HANDLER					\
+__local_syscall_error:						\
+       NEG	D0Re0, D0Re0;					\
+       ADDT	D1Re0, CPC1, #HI(_rtld_errno);			\
+       ADD	D1Re0, D1Re0, #LO(_rtld_errno) + 4;		\
+       SETD	[D1Re0], D0Re0;					\
+       NEG	D0Re0, #0x1;					\
+       MOV	PC, D1RtP;
+# else
+#  define SYSCALL_ERROR_HANDLER					\
+__local_syscall_error:						\
+	MOV	D1Re0, D1RtP;					\
+	SETL	[A0StP++], D0Re0, D1Re0;			\
+	CALLR	D1RtP, PLTJMP(___errno_location);		\
+	GETD	D1Re0, [A0StP+#-8];				\
+	NEG	D1Re0, D1Re0;					\
+	SETD	[D0Re0], D1Re0;					\
+	NEG	D0Re0, #0x1;					\
+	GETD	D1RtP, [A0StP+#-4];				\
+	SUB	A0StP, A0StP, #0x8;				\
+	MOV	PC, D1RtP;
+# endif
+#else
+# define SYSCALL_ERROR_HANDLER	/* Nothing here; code in sysdep.S is used.  */
+# define SYSCALL_ERROR ___syscall_error
+#endif
+
+#endif /* __ASSEMBLER __*/
+
+/* Pointer mangling is not yet supported for META.  */
+#define PTR_MANGLE(var) (void) (var)
+#define PTR_DEMANGLE(var) (void) (var)
diff --git a/libc/sysdeps/linux/metag/vfork.S b/libc/sysdeps/linux/metag/vfork.S
new file mode 100644
index 0000000..e1b42bb
--- /dev/null
+++ b/libc/sysdeps/linux/metag/vfork.S
@@ -0,0 +1,80 @@
+/* Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <asm/unistd.h>
+
+#define _ERRNO_H
+#include <bits/errno.h>
+#include <sys/syscall.h>
+
+#ifndef SAVE_PID
+#define SAVE_PID
+#endif
+
+#ifndef RESTORE_PID
+#define RESTORE_PID
+#endif
+
+#ifdef __NR_vfork
+#define __VFORK_NR __NR_vfork
+#else
+#define __VFORK_NR __NR_fork
+#endif
+
+/* Clone the calling process, but without copying the whole address space.
+   The calling process is suspended until the new process exits or is
+   replaced by a call to `execve'.  Return -1 for errors, 0 to the new process,
+   and the process ID of the new process to the old process.  */
+
+	.balign 4
+	.global	___vfork
+	.hidden	___vfork
+	.type	___vfork, @function
+___vfork:
+
+	SAVE_PID
+
+	MOV	D1Ar1, #0x4111	/* CLONE_VM | CLONE_VFORK | SIGCHLD */
+	MOV	D0Ar2, #0
+	MOV	D1Ar3, #0
+	MOV	D0Ar4, #0
+	MOV	D1Ar5, #0
+	MOV	D0Ar6, #0
+	MOV	D1Re0, #__NR_clone
+	SWITCH  #0x440001
+
+	RESTORE_PID
+
+	MOVT	D1Re0, #HI(-4096)
+	ADD	D1Re0, D1Re0, #LO(-4096)
+	CMP	D1Re0, D0Re0
+	BCS	error
+
+	/* Syscall worked. Return to child/parent */
+	MOV	PC, D1RtP
+
+error:
+	MOV	D1Ar1, D0Re0
+#ifdef __PIC__
+	B	___syscall_error at PLT
+#else
+	B	___syscall_error
+#endif
+	.size ___vfork,.-___vfork
+
+weak_alias(__vfork,vfork)
+libc_hidden_weak(vfork)
diff --git a/libpthread/nptl/sysdeps/metag/Makefile.arch b/libpthread/nptl/sysdeps/metag/Makefile.arch
new file mode 100644
index 0000000..9f373d2
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/Makefile.arch
@@ -0,0 +1,17 @@
+# Makefile for uClibc NPTL
+#
+# Copyright (C) 2005 Steven J. Hill <sjhill at uclibc.org>
+#
+# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+#
+
+CFLAGS-pthread_spin_init.c = -DNOT_IN_libc -DIS_IN_libpthread
+ASFLAGS-pthread_spin_lock.S = -DNOT_IN_libc -DIS_IN_libpthread
+ASFLAGS-pthread_spin_unlock.S = -DNOT_IN_libc -DIS_IN_libpthread
+ASFLAGS-pthread_spin_trylock.S = -DNOT_IN_libc -DIS_IN_libpthread
+
+ASFLAGS-pthread_spin_lock.c += -D_GNU_SOURCE
+
+CFLAGS-metag = $(SSP_ALL_CFLAGS)
+
+libc_arch_a_CSRC := libc-tls.c
diff --git a/libpthread/nptl/sysdeps/metag/dl-tls.h b/libpthread/nptl/sysdeps/metag/dl-tls.h
new file mode 100644
index 0000000..cba8fcb
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/dl-tls.h
@@ -0,0 +1,29 @@
+/* Thread-local storage handling in the ELF dynamic linker.  Meta version.
+   Copyright (C) 2002 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+
+/* Type used for the representation of TLS information in the GOT.  */
+typedef struct
+{
+  unsigned long int ti_module;
+  unsigned long int ti_offset;
+} tls_index;
+
+
+extern void *__tls_get_addr (tls_index *ti);
diff --git a/libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h b/libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h
new file mode 100644
index 0000000..eedf794
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub at redhat.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+/* Test if longjmp to JMPBUF would unwind the frame
+   containing a local variable at ADDRESS.  */
+#undef _JMPBUF_UNWINDS
+#define _JMPBUF_UNWINDS(jmpbuf, address, demangle) \
+  ((void *) (address) > (void *) demangle (jmpbuf[JB_SP]))
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) > (uintptr_t) (_jmpbuf)[JB_SP] - (_adj))
+
+#define __libc_unwind_longjmp(buf, val) __libc_longjmp (buf, val)
diff --git a/libpthread/nptl/sysdeps/metag/libc-tls.c b/libpthread/nptl/sysdeps/metag/libc-tls.c
new file mode 100644
index 0000000..d87dfe1
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/libc-tls.c
@@ -0,0 +1,31 @@
+/*
+ * Thread-local storage handling in statically linked binaries.  META version.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * Based on GNU C Library (file: libc/sysdeps/sh/libc-tls.c)
+ *
+ * Copyright (C) 2010 STMicroelectronics Ltd.
+ * Author: Filippo Arcidiacono <filippo.arcidiacono at st.com>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ *
+ */
+
+#include <sysdeps/generic/libc-tls.c>
+#include <dl-tls.h>
+
+#if defined (USE_TLS) && (USE_TLS)
+
+/* On Meta, linker optimizations are not required, so __tls_get_addr
+   can be called even in statically linked binaries.  In this case module
+   must be always 1 and PT_TLS segment exist in the binary, otherwise it
+   would not link.  */
+
+void *
+__tls_get_addr (tls_index *ti)
+{
+  dtv_t *dtv = THREAD_DTV ();
+  return (char *) dtv[1].pointer.val + ti->ti_offset;
+}
+
+#endif
diff --git a/libpthread/nptl/sysdeps/metag/metag_load_tp.S b/libpthread/nptl/sysdeps/metag/metag_load_tp.S
new file mode 100644
index 0000000..8029f22
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/metag_load_tp.S
@@ -0,0 +1 @@
+#include <../../../../ldso/ldso/metag/metag_load_tp.S>
diff --git a/libpthread/nptl/sysdeps/metag/pthread_spin_init.c b/libpthread/nptl/sysdeps/metag/pthread_spin_init.c
new file mode 100644
index 0000000..0a47981
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/pthread_spin_init.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper at redhat.com>, 2002.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* Not needed.  pthread_spin_init is an alias for pthread_spin_unlock.  */
diff --git a/libpthread/nptl/sysdeps/metag/pthread_spin_lock.S b/libpthread/nptl/sysdeps/metag/pthread_spin_lock.S
new file mode 100644
index 0000000..c206ae8
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/pthread_spin_lock.S
@@ -0,0 +1,33 @@
+/* Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+	.text
+	.global	_pthread_spin_lock
+	.type	_pthread_spin_lock,function
+_pthread_spin_lock:
+1:	LNKGETD	D0Ar2, [D1Ar1]
+	CMP	D0Ar2, #0
+	ADD	D0Ar2, D0Ar2, #1
+	LNKSETDEQ [D1Ar1], D0Ar2
+	BNE	1b
+	DEFR	D0Ar2, TXSTAT
+	ANDT	D0Ar2, D0Ar2, #HI(0x3f000000)
+	CMPT	D0Ar2, #HI(0x02000000)
+	BNZ	1b
+	MOV	D0Re0, #0
+	MOV	PC, D1RtP
+        .size   _pthread_spin_lock,.-_pthread_spin_lock
diff --git a/libpthread/nptl/sysdeps/metag/pthread_spin_trylock.S b/libpthread/nptl/sysdeps/metag/pthread_spin_trylock.S
new file mode 100644
index 0000000..ddcb364
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/pthread_spin_trylock.S
@@ -0,0 +1,37 @@
+/* Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <pthread-errnos.h>
+
+	.text
+	.global	_pthread_spin_trylock
+	.type	_pthread_spin_trylock,function
+_pthread_spin_trylock:
+1:	MOVT	D0Re0, #HI(#EBUSY)
+	ADD	D0Re0, D0Re0, #LO(#EBUSY)
+	LNKGETD	D0Ar2, [D1Ar1]
+	CMP	D0Ar2, #0
+	ADD	D0Ar2, D0Ar2, #1
+	LNKSETDEQ [D1Ar1], D0Ar2
+	BNE	2f
+	DEFR	D0Ar2, TXSTAT
+	ANDT	D0Ar2, D0Ar2, #HI(0x3f000000)
+	CMPT	D0Ar2, #HI(0x02000000)
+	BNZ	1b
+	MOV	D0Re0, #0
+2:	MOV	PC, D1RtP
+        .size   _pthread_spin_trylock,.-_pthread_spin_trylock
diff --git a/libpthread/nptl/sysdeps/metag/pthread_spin_unlock.S b/libpthread/nptl/sysdeps/metag/pthread_spin_unlock.S
new file mode 100644
index 0000000..f4c9ead
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/pthread_spin_unlock.S
@@ -0,0 +1,29 @@
+/* Copyright (C) 2013 Imagination Technologies Ltd.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+	.text
+	.global	_pthread_spin_unlock
+	.type	_pthread_spin_unlock,function
+_pthread_spin_unlock:
+	MOV	D0Re0, #0
+	SETD	[D1Ar1], D0Re0
+	MOV	PC, D1RtP
+        .size   _pthread_spin_unlock,.-_pthread_spin_unlock
+
+        /* The implementation of pthread_spin_init is identical.  */
+        .global  _pthread_spin_init
+_pthread_spin_init = _pthread_spin_unlock
diff --git a/libpthread/nptl/sysdeps/metag/pthreaddef.h b/libpthread/nptl/sysdeps/metag/pthreaddef.h
new file mode 100644
index 0000000..51625e4
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/pthreaddef.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2003 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <sysdep.h>
+
+/* Default stack size.  */
+#define ARCH_STACK_DEFAULT_SIZE	(2 * 1024 * 1024)
+
+/* Required stack pointer alignment at beginning.  */
+#define STACK_ALIGN		8
+
+/* Minimal stack size after allocating thread descriptor and guard size.  */
+#define MINIMAL_REST_STACK	2048
+
+/* Alignment requirement for TCB.  */
+#define TCB_ALIGNMENT		8
+
+
+/* Location of current stack frame.  */
+#define CURRENT_STACK_FRAME	__builtin_frame_address (0)
+
+
+/* XXX Until we have a better place keep the definitions here.  */
+#define __exit_thread_inline(val) \
+  INLINE_SYSCALL (exit, 1, (val))
diff --git a/libpthread/nptl/sysdeps/metag/tcb-offsets.sym b/libpthread/nptl/sysdeps/metag/tcb-offsets.sym
new file mode 100644
index 0000000..753b72b
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/tcb-offsets.sym
@@ -0,0 +1,15 @@
+#include <sysdep.h>
+#include <tls.h>
+
+RESULT			offsetof (struct pthread, result)
+TID			offsetof (struct pthread, tid)
+PID			offsetof (struct pthread, pid)
+CANCELHANDLING		offsetof (struct pthread, cancelhandling)
+CLEANUP_JMP_BUF		offsetof (struct pthread, cleanup_jmp_buf)
+MULTIPLE_THREADS_OFFSET	offsetof (struct pthread, header.multiple_threads)
+TLS_PRE_TCB_SIZE	sizeof (struct pthread)
+MUTEX_FUTEX		offsetof (pthread_mutex_t, __data.__lock)
+POINTER_GUARD		offsetof (tcbhead_t, pointer_guard)
+#ifndef __ASSUME_PRIVATE_FUTEX
+PRIVATE_FUTEX		offsetof (struct pthread, header.private_futex)
+#endif
diff --git a/libpthread/nptl/sysdeps/metag/tls.h b/libpthread/nptl/sysdeps/metag/tls.h
new file mode 100644
index 0000000..5fff40f
--- /dev/null
+++ b/libpthread/nptl/sysdeps/metag/tls.h
@@ -0,0 +1,163 @@
+/* Definition for thread-local data handling.  NPTL/META version.
+   Copyright (C) 2003, 2005, 2006, 2007 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _TLS_H
+#define _TLS_H
+
+#ifndef __ASSEMBLER__
+# include <stdbool.h>
+# include <stddef.h>
+# include <stdint.h>
+# include <stdlib.h>
+# include <list.h>
+# include <sysdep.h>
+# include <bits/kernel-features.h>
+
+/* Type for the dtv.  */
+typedef union dtv
+{
+  size_t counter;
+  struct
+  {
+    void *val;
+    bool is_static;
+  } pointer;
+} dtv_t;
+
+typedef struct
+{
+  dtv_t *dtv;
+  uintptr_t pointer_guard;
+} tcbhead_t;
+
+# define TLS_MULTIPLE_THREADS_IN_TCB 1
+
+#else /* __ASSEMBLER__ */
+# include <tcb-offsets.h>
+#endif /* __ASSEMBLER__ */
+
+
+/* We require TLS support in the tools.  */
+#define HAVE_TLS_SUPPORT
+#define HAVE___THREAD   1
+#define HAVE_TLS_MODEL_ATTRIBUTE       1
+/* Signal that TLS support is available.  */
+# define USE_TLS       1
+
+#ifndef __ASSEMBLER__
+
+/* Get system call information.  */
+# include <sysdep.h>
+
+/* This is the size of the initial TCB.  */
+# define TLS_INIT_TCB_SIZE sizeof (tcbhead_t)
+
+/* Alignment requirements for the initial TCB.  */
+# define TLS_INIT_TCB_ALIGN __alignof__ (tcbhead_t)
+
+/* This is the size of the TCB.  */
+# define TLS_TCB_SIZE sizeof (tcbhead_t)
+
+/* This is the size we need before TCB.  */
+# define TLS_PRE_TCB_SIZE sizeof (struct pthread)
+
+/* Alignment requirements for the TCB.  */
+# define TLS_TCB_ALIGN __alignof__ (struct pthread)
+
+/* The TLS blocks start right after the TCB.  */
+# define TLS_DTV_AT_TP	1
+
+/* Get the thread descriptor definition.  */
+# include <descr.h>
+
+/* Install the dtv pointer.  The pointer passed is to the element with
+   index -1 which contain the length.  */
+# define INSTALL_DTV(tcbp, dtvp) \
+  ((tcbhead_t *) (tcbp))->dtv = (dtvp) + 1
+
+/* Install new dtv for current thread.  */
+# define INSTALL_NEW_DTV(dtv) \
+  (((tcbhead_t *)__builtin_thread_pointer ())->dtv = (dtv))
+
+/* Return dtv of given thread descriptor.  */
+# define GET_DTV(tcbp) \
+  (((tcbhead_t *) (tcbp))->dtv)
+
+/* Code to initially initialize the thread pointer.  This might need
+   special attention since 'errno' is not yet available and if the
+   operation can cause a failure 'errno' must not be touched.  */
+# define TLS_INIT_TP(tcbp, secondcall) \
+  ({ INTERNAL_SYSCALL_DECL (err);					\
+     long result_var;							\
+     result_var = INTERNAL_SYSCALL (metag_set_tls, err, 1, (tcbp));	\
+     INTERNAL_SYSCALL_ERROR_P (result_var, err)				\
+       ? "unknown error" : NULL; })
+
+/* Return the address of the dtv for the current thread.  */
+# define THREAD_DTV() \
+  (((tcbhead_t *)__builtin_thread_pointer ())->dtv)
+
+/* Return the thread descriptor for the current thread.
+   The contained asm must *not* be marked volatile since otherwise
+   assignments like
+        struct pthread *self = thread_self();
+   do not get optimized away.  */
+# define THREAD_SELF \
+  ((struct pthread *)__builtin_thread_pointer () - 1)
+
+/* Magic for libthread_db to know how to do THREAD_SELF.  */
+# define DB_THREAD_SELF \
+  CONST_THREAD_AREA (32, sizeof (struct pthread))
+
+/* Access to data in the thread descriptor is easy.  */
+#define THREAD_GETMEM(descr, member) \
+  descr->member
+#define THREAD_GETMEM_NC(descr, member, idx) \
+  descr->member[idx]
+#define THREAD_SETMEM(descr, member, value) \
+  descr->member = (value)
+#define THREAD_SETMEM_NC(descr, member, idx, value) \
+  descr->member[idx] = (value)
+
+/* Get and set the global scope generation counter in struct pthread.  */
+#define THREAD_GSCOPE_FLAG_UNUSED 0
+#define THREAD_GSCOPE_FLAG_USED   1
+#define THREAD_GSCOPE_FLAG_WAIT   2
+#define THREAD_GSCOPE_RESET_FLAG() \
+  do									     \
+    { int __res								     \
+	= atomic_exchange_rel (&THREAD_SELF->header.gscope_flag,	     \
+			       THREAD_GSCOPE_FLAG_UNUSED);		     \
+      if (__res == THREAD_GSCOPE_FLAG_WAIT)				     \
+	lll_futex_wake (&THREAD_SELF->header.gscope_flag, 1, LLL_PRIVATE);   \
+    }									     \
+  while (0)
+#define THREAD_GSCOPE_SET_FLAG() \
+  do									     \
+    {									     \
+      THREAD_SELF->header.gscope_flag = THREAD_GSCOPE_FLAG_USED;	     \
+      atomic_write_barrier ();						     \
+    }									     \
+  while (0)
+#define THREAD_GSCOPE_WAIT() \
+  GL(dl_wait_lookup_done) ()
+
+#endif /* __ASSEMBLER__ */
+
+#endif	/* tls.h */
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile
new file mode 100644
index 0000000..43a6fad
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile
@@ -0,0 +1,13 @@
+# Makefile for uClibc NPTL
+#
+# Copyright (C) 2005 Steven J. Hill <sjhill at uclibc.org>
+#
+# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+#
+
+top_srcdir=../../../../../../../
+top_builddir=../../../../../../../
+all: objs
+include $(top_builddir)Rules.mak
+include Makefile.arch
+include $(top_srcdir)Makerules
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile.arch b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile.arch
new file mode 100644
index 0000000..7a5c874
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile.arch
@@ -0,0 +1,34 @@
+# Makefile for uClibc NPTL
+#
+# Copyright (C) 2006 Steven J. Hill <sjhill at uclibc.org>
+#
+# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+#
+
+libpthread_linux_arch_SSRC = pt-vfork.S
+libpthread_linux_arch_CSRC = pthread_once.c \
+	pt-__syscall_rt_sigaction.c pt-__syscall_error.c \
+	lowlevellock.c
+
+libc_linux_arch_CSRC = fork.c libc-lowlevellock.c
+libc_linux_arch_SSRC = clone.S vfork.S
+libc_linux_arch_SSRC-OMIT = waitpid.S
+
+CFLAGS += $(SSP_ALL_CFLAGS)
+
+CFLAGS-pthread_once.c = -DNOT_IN_libc -DIS_IN_libpthread
+CFLAGS-pt-__syscall_rt_sigaction.c = -DNOT_IN_libc -DIS_IN_libpthread
+CFLAGS-lowlevellock.c = -DNOT_IN_libc -DIS_IN_libpthread
+CFLAGS-pt-__syscall_error.c =  -DNOT_IN_libc -DIS_IN_libpthread
+ASFLAGS-vfork.S = -DIS_IN_libc -DNOT_IN_libpthread
+ASFLAGS-clone.S = -DIS_IN_libc -DNOT_IN_libpthread
+ASFLAGS-pt-vfork.S = -DNOT_IN_libc -DIS_IN_libpthread
+
+ifeq ($(UCLIBC_HAS_STDIO_FUTEXES),y)
+CFLAGS-fork.c = -D__USE_STDIO_FUTEXES__
+endif
+CFLAGS-OMIT-fork.c = -DNOT_IN_libc -DIS_IN_libpthread
+CFLAGS-OMIT-libc-lowlevellock.c = -DNOT_IN_libc -DIS_IN_libpthread
+# We always compile it in arm mode because of SAVE_PID macro
+# This macro should be alternatively implemented in THUMB
+# assembly.
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/pthreadtypes.h b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/pthreadtypes.h
new file mode 100644
index 0000000..e1b115c
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/pthreadtypes.h
@@ -0,0 +1,181 @@
+/* Copyright (C) 2002, 2003, 2004, 2005, 2006 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _BITS_PTHREADTYPES_H
+#define _BITS_PTHREADTYPES_H	1
+
+#include <endian.h>
+
+#define __SIZEOF_PTHREAD_ATTR_T 36
+#define __SIZEOF_PTHREAD_MUTEX_T 24
+#define __SIZEOF_PTHREAD_MUTEXATTR_T 4
+#define __SIZEOF_PTHREAD_COND_T 48
+#define __SIZEOF_PTHREAD_COND_COMPAT_T 12
+#define __SIZEOF_PTHREAD_CONDATTR_T 4
+#define __SIZEOF_PTHREAD_RWLOCK_T 32
+#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
+#define __SIZEOF_PTHREAD_BARRIER_T 20
+#define __SIZEOF_PTHREAD_BARRIERATTR_T 4
+
+
+/* Thread identifiers.  The structure of the attribute type is not
+   exposed on purpose.  */
+typedef unsigned long int pthread_t;
+
+
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_ATTR_T];
+  long int __align;
+} pthread_attr_t;
+
+
+typedef struct __pthread_internal_slist
+{
+  struct __pthread_internal_slist *__next;
+} __pthread_slist_t;
+
+
+/* Data structures for mutex handling.  The structure of the attribute
+   type is not exposed on purpose.  */
+typedef union
+{
+  struct __pthread_mutex_s
+  {
+    int __lock;
+    unsigned int __count;
+    int __owner;
+    /* KIND must stay at this position in the structure to maintain
+       binary compatibility.  */
+    int __kind;
+    unsigned int __nusers;
+    __extension__ union
+    {
+      int __spins;
+      __pthread_slist_t __list;
+    };
+  } __data;
+  char __size[__SIZEOF_PTHREAD_MUTEX_T];
+  long int __align;
+} pthread_mutex_t;
+
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
+  long int __align;
+} pthread_mutexattr_t;
+
+
+/* Data structure for conditional variable handling.  The structure of
+   the attribute type is not exposed on purpose.  */
+typedef union
+{
+  struct
+  {
+    int __lock;
+    unsigned int __futex;
+    __extension__ unsigned long long int __total_seq;
+    __extension__ unsigned long long int __wakeup_seq;
+    __extension__ unsigned long long int __woken_seq;
+    void *__mutex;
+    unsigned int __nwaiters;
+    unsigned int __broadcast_seq;
+  } __data;
+  char __size[__SIZEOF_PTHREAD_COND_T];
+  __extension__ long long int __align;
+} pthread_cond_t;
+
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_CONDATTR_T];
+  long int __align;
+} pthread_condattr_t;
+
+
+/* Keys for thread-specific data */
+typedef unsigned int pthread_key_t;
+
+
+/* Once-only execution */
+typedef int pthread_once_t;
+
+
+#if defined __USE_UNIX98 || defined __USE_XOPEN2K
+/* Data structure for read-write lock variable handling.  The
+   structure of the attribute type is not exposed on purpose.  */
+typedef union
+{
+  struct
+  {
+    int __lock;
+    unsigned int __nr_readers;
+    unsigned int __readers_wakeup;
+    unsigned int __writer_wakeup;
+    unsigned int __nr_readers_queued;
+    unsigned int __nr_writers_queued;
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned char __pad1;
+    unsigned char __pad2;
+    unsigned char __shared;
+    /* FLAGS must stay at this position in the structure to maintain
+       binary compatibility.  */
+    unsigned char __flags;
+#else
+    /* FLAGS must stay at this position in the structure to maintain
+       binary compatibility.  */
+    unsigned char __flags;
+    unsigned char __shared;
+    unsigned char __pad1;
+    unsigned char __pad2;
+#endif
+    int __writer;
+  } __data;
+  char __size[__SIZEOF_PTHREAD_RWLOCK_T];
+  long int __align;
+} pthread_rwlock_t;
+
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
+  long int __align;
+} pthread_rwlockattr_t;
+#endif
+
+
+#ifdef __USE_XOPEN2K
+/* POSIX spinlock data type.  */
+typedef volatile int pthread_spinlock_t;
+
+
+/* POSIX barriers data type.  The structure of the type is
+   deliberately not exposed.  */
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_BARRIER_T];
+  long int __align;
+} pthread_barrier_t;
+
+typedef union
+{
+  char __size[__SIZEOF_PTHREAD_BARRIERATTR_T];
+  int __align;
+} pthread_barrierattr_t;
+#endif
+
+
+#endif	/* bits/pthreadtypes.h */
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/semaphore.h b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/semaphore.h
new file mode 100644
index 0000000..dadfac2
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/semaphore.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002, 2005, 2007 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead."
+#endif
+
+
+#define __SIZEOF_SEM_T	16
+
+
+/* Value returned if `sem_open' failed.  */
+#define SEM_FAILED      ((sem_t *) 0)
+
+
+typedef union
+{
+  char __size[__SIZEOF_SEM_T];
+  long int __align;
+} sem_t;
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/clone.S b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/clone.S
new file mode 100644
index 0000000..075743a
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/clone.S
@@ -0,0 +1,3 @@
+#define RESET_PID
+#include <tcb-offsets.h>
+#include "../../../../../../../libc/sysdeps/linux/metag/clone.S"
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/createthread.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/createthread.c
new file mode 100644
index 0000000..2d43559
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/createthread.c
@@ -0,0 +1,23 @@
+/* Copyright (C) 2005 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* Value passed to 'clone' for initialization of the thread register.  */
+#define TLS_VALUE (pd + 1)
+
+/* Get the real implementation.	 */
+#include <sysdeps/pthread/createthread.c>
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/fork.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/fork.c
new file mode 100644
index 0000000..41776f9
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/fork.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Phil Blundell <pb at nexus.co.uk>, 2005
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <sched.h>
+#include <signal.h>
+#include <sysdep.h>
+#include <tls.h>
+
+
+#define ARCH_FORK()							\
+  INLINE_SYSCALL (clone, 5,						\
+		  CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD,	\
+		  NULL, NULL, &THREAD_SELF->tid, NULL)
+
+#include "../fork.c"
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/libc-lowlevellock.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/libc-lowlevellock.c
new file mode 100644
index 0000000..b192822
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/libc-lowlevellock.c
@@ -0,0 +1,21 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Paul Mackerras <paulus at au.ibm.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* No difference to lowlevellock.c, except we lose a couple of functions.  */
+#include "lowlevellock.c"
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.c
new file mode 100644
index 0000000..8c8955e
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.c
@@ -0,0 +1,134 @@
+/* low level locking for pthread library.  Generic futex-using version.
+   Copyright (C) 2003, 2005, 2007 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <sysdep.h>
+#include <lowlevellock.h>
+#include <sys/time.h>
+#include <tls.h>
+
+void
+__lll_lock_wait_private (int *futex)
+{
+  do
+    {
+      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
+      if (oldval != 0)
+	lll_futex_wait (futex, 2, LLL_PRIVATE);
+    }
+  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
+}
+
+
+/* These functions don't get included in libc.so  */
+#ifdef IS_IN_libpthread
+void
+__lll_lock_wait (int *futex, int private)
+{
+  do
+    {
+      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
+      if (oldval != 0)
+	lll_futex_wait (futex, 2, private);
+    }
+  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
+}
+
+
+int
+__lll_timedlock_wait (int *futex, const struct timespec *abstime, int private)
+{
+  struct timespec rt;
+
+  /* Reject invalid timeouts.  */
+  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
+    return EINVAL;
+
+  /* Upgrade the lock.  */
+  if (atomic_exchange_acq (futex, 2) == 0)
+    return 0;
+
+  do
+    {
+      struct timeval tv;
+
+      /* Get the current time.  */
+      (void) gettimeofday (&tv, NULL);
+
+      /* Compute relative timeout.  */
+      rt.tv_sec = abstime->tv_sec - tv.tv_sec;
+      rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
+      if (rt.tv_nsec < 0)
+	{
+	  rt.tv_nsec += 1000000000;
+	  --rt.tv_sec;
+	}
+
+      /* Already timed out?  */
+      if (rt.tv_sec < 0)
+	return ETIMEDOUT;
+
+      // XYZ: Lost the lock to check whether it was private.
+      lll_futex_timed_wait (futex, 2, &rt, private);
+    }
+  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
+
+  return 0;
+}
+
+
+int
+__lll_timedwait_tid (int *tidp, const struct timespec *abstime)
+{
+  int tid;
+
+  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
+    return EINVAL;
+
+  /* Repeat until thread terminated.  */
+  while ((tid = *tidp) != 0)
+    {
+      struct timeval tv;
+      struct timespec rt;
+
+      /* Get the current time.  */
+      (void) gettimeofday (&tv, NULL);
+
+      /* Compute relative timeout.  */
+      rt.tv_sec = abstime->tv_sec - tv.tv_sec;
+      rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
+      if (rt.tv_nsec < 0)
+	{
+	  rt.tv_nsec += 1000000000;
+	  --rt.tv_sec;
+	}
+
+      /* Already timed out?  */
+      if (rt.tv_sec < 0)
+	return ETIMEDOUT;
+
+      /* Wait until thread terminates.  */
+      // XYZ: Lost the lock to check whether it was private.
+      if (lll_futex_timed_wait (tidp, tid, &rt, LLL_SHARED) == -ETIMEDOUT)
+	return ETIMEDOUT;
+    }
+
+  return 0;
+}
+#endif
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.h b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.h
new file mode 100644
index 0000000..4c7d08c
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.h
@@ -0,0 +1,282 @@
+/* Copyright (C) 2005, 2006, 2007, 2008, 2009 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _LOWLEVELLOCK_H
+#define _LOWLEVELLOCK_H	1
+
+#include <time.h>
+#include <sys/param.h>
+#include <bits/pthreadtypes.h>
+#include <atomic.h>
+#include <sysdep.h>
+#include <bits/kernel-features.h>
+
+#define FUTEX_WAIT		0
+#define FUTEX_WAKE		1
+#define FUTEX_REQUEUE		3
+#define FUTEX_CMP_REQUEUE	4
+#define FUTEX_WAKE_OP		5
+#define FUTEX_OP_CLEAR_WAKE_IF_GT_ONE	((4 << 24) | 1)
+#define FUTEX_LOCK_PI		6
+#define FUTEX_UNLOCK_PI		7
+#define FUTEX_TRYLOCK_PI	8
+#define FUTEX_WAIT_BITSET	9
+#define FUTEX_WAKE_BITSET	10
+#define FUTEX_PRIVATE_FLAG	128
+#define FUTEX_CLOCK_REALTIME	256
+
+#define FUTEX_BITSET_MATCH_ANY	0xffffffff
+
+/* Values for 'private' parameter of locking macros.  Yes, the
+   definition seems to be backwards.  But it is not.  The bit will be
+   reversed before passing to the system call.  */
+#define LLL_PRIVATE	0
+#define LLL_SHARED	FUTEX_PRIVATE_FLAG
+
+
+#if !defined NOT_IN_libc || defined IS_IN_rtld
+/* In libc.so or ld.so all futexes are private.  */
+# ifdef __ASSUME_PRIVATE_FUTEX
+#  define __lll_private_flag(fl, private) \
+  ((fl) | FUTEX_PRIVATE_FLAG)
+# else
+#  define __lll_private_flag(fl, private) \
+  ((fl) | THREAD_GETMEM (THREAD_SELF, header.private_futex))
+# endif
+#else
+# ifdef __ASSUME_PRIVATE_FUTEX
+#  define __lll_private_flag(fl, private) \
+  (((fl) | FUTEX_PRIVATE_FLAG) ^ (private))
+# else
+#  define __lll_private_flag(fl, private) \
+  (__builtin_constant_p (private)					      \
+   ? ((private) == 0							      \
+      ? ((fl) | THREAD_GETMEM (THREAD_SELF, header.private_futex))	      \
+      : (fl))								      \
+   : ((fl) | (((private) ^ FUTEX_PRIVATE_FLAG)				      \
+	      & THREAD_GETMEM (THREAD_SELF, header.private_futex))))
+# endif	      
+#endif
+
+
+#define lll_futex_wait(futexp, val, private) \
+  lll_futex_timed_wait(futexp, val, NULL, private)
+
+#define lll_futex_timed_wait(futexp, val, timespec, private) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    long int __ret;							      \
+    __ret = INTERNAL_SYSCALL (futex, __err, 4, (futexp),		      \
+			      __lll_private_flag (FUTEX_WAIT, private),	      \
+			      (val), (timespec));			      \
+    __ret;								      \
+  })
+
+#define lll_futex_wake(futexp, nr, private) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    long int __ret;							      \
+    __ret = INTERNAL_SYSCALL (futex, __err, 4, (futexp),		      \
+			      __lll_private_flag (FUTEX_WAKE, private),	      \
+			      (nr), 0);					      \
+    __ret;								      \
+  })
+
+#define lll_robust_dead(futexv, private) \
+  do									      \
+    {									      \
+      int *__futexp = &(futexv);					      \
+      atomic_or (__futexp, FUTEX_OWNER_DIED);				      \
+      lll_futex_wake (__futexp, 1, private);				      \
+    }									      \
+  while (0)
+
+/* Returns non-zero if error happened, zero if success.  */
+#define lll_futex_requeue(futexp, nr_wake, nr_move, mutex, val, private) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    long int __ret;							      \
+    __ret = INTERNAL_SYSCALL (futex, __err, 6, (futexp),		      \
+			      __lll_private_flag (FUTEX_CMP_REQUEUE, private),\
+			      (nr_wake), (nr_move), (mutex), (val));	      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err);				      \
+  })
+
+
+/* Returns non-zero if error happened, zero if success.  */
+#define lll_futex_wake_unlock(futexp, nr_wake, nr_wake2, futexp2, private) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    long int __ret;							      \
+    __ret = INTERNAL_SYSCALL (futex, __err, 6, (futexp),		      \
+			      __lll_private_flag (FUTEX_WAKE_OP, private),    \
+			      (nr_wake), (nr_wake2), (futexp2),		      \
+			      FUTEX_OP_CLEAR_WAKE_IF_GT_ONE);		      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err);				      \
+  })
+
+
+#define lll_trylock(lock)	\
+  atomic_compare_and_exchange_val_acq(&(lock), 1, 0)
+
+#define lll_cond_trylock(lock)	\
+  atomic_compare_and_exchange_val_acq(&(lock), 2, 0)
+
+#define __lll_robust_trylock(futex, id) \
+  (atomic_compare_and_exchange_val_acq (futex, id, 0) != 0)
+#define lll_robust_trylock(lock, id) \
+  __lll_robust_trylock (&(lock), id)
+
+extern void __lll_lock_wait_private (int *futex) attribute_hidden;
+extern void __lll_lock_wait (int *futex, int private) attribute_hidden;
+extern int __lll_robust_lock_wait (int *futex, int private) attribute_hidden;
+
+#define __lll_lock(futex, private)					      \
+  ((void) ({								      \
+    int *__futex = (futex);						      \
+    if (__builtin_expect (atomic_compare_and_exchange_val_acq (__futex,       \
+								1, 0), 0))    \
+      {									      \
+	if (__builtin_constant_p (private) && (private) == LLL_PRIVATE)	      \
+	  __lll_lock_wait_private (__futex);				      \
+	else								      \
+	  __lll_lock_wait (__futex, private);				      \
+      }									      \
+  }))
+#define lll_lock(futex, private) __lll_lock (&(futex), private)
+
+
+#define __lll_robust_lock(futex, id, private)				      \
+  ({									      \
+    int *__futex = (futex);						      \
+    int __val = 0;							      \
+									      \
+    if (__builtin_expect (atomic_compare_and_exchange_bool_acq (__futex, id,  \
+								0), 0))	      \
+      __val = __lll_robust_lock_wait (__futex, private);		      \
+    __val;								      \
+  })
+#define lll_robust_lock(futex, id, private) \
+  __lll_robust_lock (&(futex), id, private)
+
+
+#define __lll_cond_lock(futex, private)					      \
+  ((void) ({								      \
+    int *__futex = (futex);						      \
+    if (__builtin_expect (atomic_exchange_acq (__futex, 2), 0))		      \
+      __lll_lock_wait (__futex, private);				      \
+  }))
+#define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private)
+
+
+#define lll_robust_cond_lock(futex, id, private) \
+  __lll_robust_lock (&(futex), (id) | FUTEX_WAITERS, private)
+
+
+extern int __lll_timedlock_wait (int *futex, const struct timespec *,
+				 int private) attribute_hidden;
+extern int __lll_robust_timedlock_wait (int *futex, const struct timespec *,
+					int private) attribute_hidden;
+
+#define __lll_timedlock(futex, abstime, private)			      \
+  ({									      \
+     int *__futex = (futex);						      \
+     int __val = 0;							      \
+									      \
+     if (__builtin_expect (atomic_exchange_acq (__futex, 1), 0))	      \
+       __val = __lll_timedlock_wait (__futex, abstime, private);	      \
+     __val;								      \
+  })
+#define lll_timedlock(futex, abstime, private) \
+  __lll_timedlock (&(futex), abstime, private)
+
+
+#define __lll_robust_timedlock(futex, abstime, id, private)		      \
+  ({									      \
+    int *__futex = (futex);						      \
+    int __val = 0;							      \
+									      \
+    if (__builtin_expect (atomic_compare_and_exchange_bool_acq (__futex, id,  \
+								0), 0))	      \
+      __val = __lll_robust_timedlock_wait (__futex, abstime, private);	      \
+    __val;								      \
+  })
+#define lll_robust_timedlock(futex, abstime, id, private) \
+  __lll_robust_timedlock (&(futex), abstime, id, private)
+
+
+#define __lll_unlock(futex, private) \
+  (void)							\
+    ({ int *__futex = (futex);					\
+       int __oldval = atomic_exchange_rel (__futex, 0);		\
+       if (__builtin_expect (__oldval > 1, 0))			\
+	 lll_futex_wake (__futex, 1, private);			\
+    })
+#define lll_unlock(futex, private) __lll_unlock(&(futex), private)
+
+
+#define __lll_robust_unlock(futex, private) \
+  (void)							\
+    ({ int *__futex = (futex);					\
+       int __oldval = atomic_exchange_rel (__futex, 0);		\
+       if (__builtin_expect (__oldval & FUTEX_WAITERS, 0))	\
+	 lll_futex_wake (__futex, 1, private);			\
+    })
+#define lll_robust_unlock(futex, private) \
+  __lll_robust_unlock(&(futex), private)
+
+
+#define lll_islocked(futex) \
+  (futex != 0)
+
+
+/* Our internal lock implementation is identical to the binary-compatible
+   mutex implementation. */
+
+/* Initializers for lock.  */
+#define LLL_LOCK_INITIALIZER		(0)
+#define LLL_LOCK_INITIALIZER_LOCKED	(1)
+
+/* The states of a lock are:
+    0  -  untaken
+    1  -  taken by one user
+   >1  -  taken by more users */
+
+/* The kernel notifies a process which uses CLONE_CLEARTID via futex
+   wakeup when the clone terminates.  The memory location contains the
+   thread ID while the clone is running and is reset to zero
+   afterwards.	*/
+#define lll_wait_tid(tid) \
+  do {					\
+    __typeof (tid) __tid;		\
+    while ((__tid = (tid)) != 0)	\
+      lll_futex_wait (&(tid), __tid, LLL_SHARED);\
+  } while (0)
+
+extern int __lll_timedwait_tid (int *, const struct timespec *)
+     attribute_hidden;
+
+#define lll_timedwait_tid(tid, abstime) \
+  ({							\
+    int __res = 0;					\
+    if ((tid) != 0)					\
+      __res = __lll_timedwait_tid (&(tid), (abstime));	\
+    __res;						\
+  })
+
+#endif	/* lowlevellock.h */
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_error.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_error.c
new file mode 100644
index 0000000..81d042a
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_error.c
@@ -0,0 +1 @@
+#include <../../../../../../../libc/sysdeps/linux/metag/__syscall_error.c>
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_rt_sigaction.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_rt_sigaction.c
new file mode 100644
index 0000000..50137c8
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_rt_sigaction.c
@@ -0,0 +1 @@
+#include <../../../../../../../libc/sysdeps/linux/common/__syscall_rt_sigaction.c>
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-gettimeofday.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-gettimeofday.c
new file mode 100644
index 0000000..08710f1
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-gettimeofday.c
@@ -0,0 +1,5 @@
+#include <sys/syscall.h>
+#include <sys/time.h>
+
+int gettimeofday (struct timeval *, struct timezone *) attribute_hidden;
+_syscall2(int, gettimeofday, struct timeval *, tv, struct timezone *, tz);
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S
new file mode 100644
index 0000000..489c749
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S
@@ -0,0 +1,51 @@
+/* Copyright (C) 2005 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <tcb-offsets.h>
+#include <asm/unistd.h>
+
+#ifdef __PIC__
+#define __VFORK_METAG_LOAD_TP ___metag_load_tp at PLT
+#else
+#define __VFORK_METAG_LOAD_TP ___metag_load_tp
+#endif
+
+/* Save the PID value.  */
+#define SAVE_PID \
+	SETL	[A0StP++], D0FrT, D1RtP; \
+	CALLR	D1RtP, __VFORK_METAG_LOAD_TP; \
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE; \
+	GETD	D0FrT, [D0Re0 + #PID]; \
+	NEG	D0FrT, D0FrT; \
+	SETD	[D0Re0 + #PID], D0FrT; \
+	GETL	D0FrT, D1RtP, [--A0StP];
+
+#define RESTORE_PID \
+	CMP 	D0Re0, #0; \
+	BEQ	1f; \
+	MSETL	[A0StP++], D0Re0, D0FrT; \
+	CALLR	D1RtP, __VFORK_METAG_LOAD_TP; \
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE; \
+	GETD	D0FrT, [D0Re0 + #PID]; \
+	NEG	D0FrT, D0FrT; \
+	SETD	[D0Re0 + #PID], D0FrT; \
+	GETL	D0FrT, D1RtP, [--A0StP]; \
+	GETL	D0Re0, D1Re0, [--A0StP]; \
+1:
+
+#include <../../../../../../../libc/sysdeps/linux/metag/vfork.S>
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pthread_once.c b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pthread_once.c
new file mode 100644
index 0000000..e977a7d
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/pthread_once.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2004, 2005 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "pthreadP.h"
+#include <lowlevellock.h>
+
+unsigned long int __fork_generation attribute_hidden;
+
+static void
+clear_once_control (void *arg)
+{
+  pthread_once_t *once_control = (pthread_once_t *) arg;
+
+  *once_control = 0;
+  lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
+}
+
+int
+attribute_protected
+__pthread_once (pthread_once_t *once_control, void (*init_routine) (void))
+{
+  for (;;)
+    {
+      int oldval;
+      int newval;
+
+      /* Pseudo code:
+	 newval = __fork_generation | 1;
+	 oldval = *once_control;
+	 if ((oldval & 2) == 0)
+	   *once_control = newval;
+	 Do this atomically.
+      */
+      do
+	{
+	  newval = __fork_generation | 1;
+	  oldval = *once_control;
+	  if (oldval & 2)
+	    break;
+	} while (atomic_compare_and_exchange_val_acq (once_control, newval, oldval) != oldval);
+
+      /* Check if the initializer has already been done.  */
+      if ((oldval & 2) != 0)
+	return 0;
+
+      /* Check if another thread already runs the initializer.	*/
+      if ((oldval & 1) == 0)
+	break;
+
+      /* Check whether the initializer execution was interrupted by a fork.  */
+      if (oldval != newval)
+	break;
+
+      /* Same generation, some other thread was faster. Wait.  */
+      lll_futex_wait (once_control, oldval, LLL_PRIVATE);
+    }
+
+  /* This thread is the first here.  Do the initialization.
+     Register a cleanup handler so that in case the thread gets
+     interrupted the initialization can be restarted.  */
+  pthread_cleanup_push (clear_once_control, once_control);
+
+  init_routine ();
+
+  pthread_cleanup_pop (0);
+
+  /* Say that the initialisation is done.  */
+  *once_control = __fork_generation | 2;
+
+  /* Wake up all other threads.  */
+  lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
+
+  return 0;
+}
+weak_alias (__pthread_once, pthread_once)
+strong_alias (__pthread_once, __pthread_once_internal)
+
+#if defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__PIC__)
+/* When statically linked, if pthread_create is used, this file
+   will be brought in.  The exception handling code in GCC assumes
+   that if pthread_create is available, so are these.  */
+const void *include_pthread_getspecific attribute_hidden = pthread_getspecific;
+const void *include_pthread_setspecific attribute_hidden = pthread_setspecific;
+const void *include_pthread_key_create attribute_hidden = pthread_key_create;
+#endif
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/sysdep-cancel.h b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/sysdep-cancel.h
new file mode 100644
index 0000000..853ec36
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/sysdep-cancel.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2003, 2004, 2005, 2009 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <tls.h>
+#include <sysdep.h>
+#ifndef __ASSEMBLER__
+# include <pthreadP.h>
+#endif
+
+#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
+
+/* NOTE: We do mark syscalls with unwind annotations, for the benefit of
+   cancellation; but they're really only accurate at the point of the
+   syscall.  The ARM unwind directives are not rich enough without adding
+   a custom personality function.  */
+
+#ifdef __ASSEMBLER__
+#undef ret
+#define ret								\
+   CMP D0Re0, #-4095;							\
+   MOVLO PC, D1RtP;							\
+   MOV D1Ar1, D0Re0;							\
+   B SYSCALL_ERROR;
+#endif /* __ASSEMBLER__ */
+
+# undef PSEUDO
+# define PSEUDO(name, syscall_name, args)				\
+  .section ".text";							\
+  .type ___##syscall_name##_nocancel,%function;				\
+  .globl ___##syscall_name##_nocancel;					\
+  ___##syscall_name##_nocancel:						\
+    cfi_startproc;							\
+    DO_CALL (syscall_name, args);					\
+    MOV PC, D1RtP;							\
+    cfi_endproc;							\
+  .size ___##syscall_name##_nocancel,.-___##syscall_name##_nocancel;	\
+    .globl _##name;							\
+    .type _##name, @function;						\
+name##:									\
+_##name##:								\
+    DOCARGS_##args;							\
+    SINGLE_THREAD_P;							\
+    UNDOCARGS_##args;							\
+    BNE .Lpseudo_cancel;						\
+    cfi_remember_state;							\
+    DO_CALL (syscall_name, 0);						\
+    ret									\
+    cfi_restore_state;							\
+  .Lpseudo_cancel:							\
+    MSETL [A0StP++], D0FrT, D0.5;					\
+    DOCARGS_##args;	/* save syscall args etc. around CENABLE.  */	\
+    CENABLE;								\
+    MOV D0FrT, D0Re0;	/* put mask in safe place.  */			\
+    UNDOCARGS_##args;	/* restore syscall args.  */			\
+    DO_CALL(syscall_name, 0);	/* do the call.  */			\
+    MOV D0.5, D0Re0;	/* save syscall return value.  */		\
+    MOV D1Ar1, D0FrT;	/* get mask back.  */				\
+    CDISABLE;								\
+    MOV D0Re0, D0.5;	/* retrieve return value.  */			\
+    GETL D0.5, D1.5, [--A0StP];						\
+    GETL D0FrT, D1RtP, [--A0StP];
+
+# define DOCARGS_0
+# define UNDOCARGS_0
+
+# define DOCARGS_1 \
+  SETL [A0StP++], D1Ar1, D0Ar2
+# define UNDOCARGS_1 \
+  GETL D1Ar1, D0Ar2, [--A0StP]
+
+# define DOCARGS_2 DOCARGS_1
+
+# define UNDOCARGS_2 UNDOCARGS_2
+
+# define DOCARGS_3 \
+  MSETL [A0StP++], D1Ar1, D1Ar3
+
+# define UNDOCARGS_3 \
+  GETL D1Ar1, D0Ar2, [--A0StP];		\
+  GETL D1Ar3, D0Ar4, [--A0StP]
+
+# define DOCARGS_4 DOCARGS_3
+# define UNDOCARGS_4 UNDOCARGS_3
+
+# define DOCARGS_5 \
+  MSETL [A0StP++], D1Ar1, D1Ar3, D1Ar5
+# define UNDOCARGS_5 \
+  GETL D1Ar1, D0Ar2, [--A0StP];		\
+  GETL D1Ar3, D0Ar4, [--A0StP];		\
+  GETL D1Ar5, D0Ar6, [--A0StP]
+
+# define DOCARGS_6 DOCARGS_5
+# define UNDOCARGS_6 UNDOCARGS_5
+
+# ifdef IS_IN_libpthread
+#  define CENABLE	CALLR D1RtP, ___pthread_enable_asynccancel at PLT
+#  define CDISABLE	CALLR D1RtP, ___pthread_disable_asynccancel at PLT
+#  define __local_multiple_threads __pthread_multiple_threads
+# elif !defined NOT_IN_libc
+#  define CENABLE	CALLR D1RtP, ___libc_enable_asynccancel at PLT
+#  define CDISABLE	CALLR D1RtP, ___libc_disable_asynccancel at PLT
+#  define __local_multiple_threads __libc_multiple_threads
+# elif defined IS_IN_librt
+#  define CENABLE	CALLR D1RtP, ___librt_enable_asynccancel at PLT
+#  define CDISABLE	CALLR D1RtP, ___librt_disable_asynccancel at PLT
+# else
+#  error Unsupported library
+# endif
+
+#ifndef __ASSEMBLER__
+#   define SINGLE_THREAD_P						\
+  __builtin_expect (THREAD_GETMEM (THREAD_SELF,				\
+				   header.multiple_threads) == 0, 1)
+#else
+#  define SINGLE_THREAD_P \
+	SETL	[A0StP++], D0FrT, D1RtP; \
+	CALLR	D1RtP, ___metag_load_tp at PLT; \
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE;	\
+	GETD	D0Re0, [D0Re0 + #MULTIPLE_THREADS_OFFSET]; \
+	CMP	D0Re0, #0; \
+	GETL	D0FrT, D1RtP, [--A0StP]
+#endif
+
+
+#elif !defined __ASSEMBLER__
+
+/* For rtld, et cetera.  */
+# define SINGLE_THREAD_P 1
+# define NO_CANCELLATION 1
+
+#endif
+
+#ifndef __ASSEMBLER__
+# define RTLD_SINGLE_THREAD_P \
+  __builtin_expect (THREAD_GETMEM (THREAD_SELF, \
+				   header.multiple_threads) == 0, 1)
+#endif
diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/metag/vfork.S b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/vfork.S
new file mode 100644
index 0000000..51fcfbb
--- /dev/null
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/metag/vfork.S
@@ -0,0 +1,56 @@
+/* Copyright (C) 2005 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 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <tcb-offsets.h>
+#include <asm/unistd.h>
+
+#ifdef __PIC__
+#define __VFORK_METAG_LOAD_TP ___metag_load_tp at PLT
+#else
+#define __VFORK_METAG_LOAD_TP ___metag_load_tp
+#endif
+
+/* Save the PID value.  */
+#define SAVE_PID \
+	SETL	[A0StP++], D0FrT, D1RtP; \
+	CALLR	D1RtP, __VFORK_METAG_LOAD_TP; \
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE; \
+	GETD	D0FrT, [D0Re0 + #PID]; \
+	NEGS	D0FrT, D0FrT; \
+	BNZ	1f; \
+	MOVT	D0FrT, #0x8000; \
+1:	SETD	[D0Re0 + #PID], D0FrT; \
+	GETL	D0FrT, D1RtP, [--A0StP];
+
+#define RESTORE_PID \
+	CMP 	D0Re0, #0; \
+	BEQ	1f; \
+	MSETL	[A0StP++], D0Re0, D0FrT; \
+	CALLR	D1RtP, __VFORK_METAG_LOAD_TP; \
+	SUB	D0Re0, D0Re0, #TLS_PRE_TCB_SIZE; \
+	GETD	D0FrT, [D0Re0 + #PID]; \
+	NEG	D0FrT, D0FrT; \
+	MOVT	D1Re0, #0x8000; \
+	CMP 	D0FrT, D1Re0; \
+	XOREQ	D0FrT, D0FrT, D0FrT; \
+	SETD	[D0Re0 + #PID], D0FrT; \
+	GETL	D0FrT, D1RtP, [--A0StP]; \
+	GETL	D0Re0, D1Re0, [--A0StP]; \
+1:
+
+#include <../../../../../../../libc/sysdeps/linux/metag/vfork.S>
-- 
1.8.1.2




More information about the uClibc mailing list