summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2010-11-18 14:58:01 +0000
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2013-03-14 22:47:08 +0100
commitdaecc9a410a6f23d80daf8ce3afd280fea329e63 (patch)
tree8d603dd538809431cc01b9e656c827d1f0709052
parent20221281b3d67880439cd1d16c151f4528d034fb (diff)
metag: Add NPTL support
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com> Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
-rw-r--r--include/elf.h5
-rw-r--r--ldso/ldso/metag/dl-debug.h8
-rw-r--r--ldso/ldso/metag/dl-startup.h5
-rw-r--r--ldso/ldso/metag/dl-sysdep.h19
-rw-r--r--ldso/ldso/metag/elfinterp.c119
-rw-r--r--ldso/ldso/metag/metag_load_tp.S20
-rw-r--r--libc/sysdeps/linux/common/Makefile.in1
-rw-r--r--libc/sysdeps/linux/metag/Makefile.arch4
-rw-r--r--libc/sysdeps/linux/metag/bits/atomic.h66
-rw-r--r--libc/sysdeps/linux/metag/bits/syscalls.h73
-rw-r--r--libc/sysdeps/linux/metag/clone.S50
-rw-r--r--libc/sysdeps/linux/metag/crt1.S3
-rw-r--r--libc/sysdeps/linux/metag/libc-metag_load_tp.S7
-rw-r--r--libc/sysdeps/linux/metag/sysdep.h59
-rw-r--r--libc/sysdeps/linux/metag/vfork.S67
-rw-r--r--libpthread/nptl/sysdeps/metag/Makefile.arch17
-rw-r--r--libpthread/nptl/sysdeps/metag/dl-tls.h29
-rw-r--r--libpthread/nptl/sysdeps/metag/jmpbuf-unwind.h36
-rw-r--r--libpthread/nptl/sysdeps/metag/libc-tls.c33
-rw-r--r--libpthread/nptl/sysdeps/metag/metag_load_tp.S7
-rw-r--r--libpthread/nptl/sysdeps/metag/pthread_spin_init.c20
-rw-r--r--libpthread/nptl/sysdeps/metag/pthread_spin_lock.S20
-rw-r--r--libpthread/nptl/sysdeps/metag/pthread_spin_trylock.S24
-rw-r--r--libpthread/nptl/sysdeps/metag/pthread_spin_unlock.S16
-rw-r--r--libpthread/nptl/sysdeps/metag/pthreaddef.h40
-rw-r--r--libpthread/nptl/sysdeps/metag/tcb-offsets.sym15
-rw-r--r--libpthread/nptl/sysdeps/metag/tls.h163
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile13
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/Makefile.arch34
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/pthreadtypes.h181
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/bits/semaphore.h35
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/clone.S9
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/createthread.c23
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/fork.c31
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/libc-lowlevellock.c21
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.c134
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/lowlevellock.h279
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_error.c7
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-__syscall_rt_sigaction.c7
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-gettimeofday.c11
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/pt-vfork.S51
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/pthread_once.c100
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/sysdep-cancel.h152
-rw-r--r--libpthread/nptl/sysdeps/unix/sysv/linux/metag/vfork.S56
44 files changed, 1975 insertions, 95 deletions
diff --git a/include/elf.h b/include/elf.h
index 877d08194..cbb4da218 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 5981d7c73..46c257c5c 100644
--- a/ldso/ldso/metag/dl-debug.h
+++ b/ldso/ldso/metag/dl-debug.h
@@ -17,11 +17,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 8dbf747e1..32b2e4b74 100644
--- a/ldso/ldso/metag/dl-startup.h
+++ b/ldso/ldso/metag/dl-startup.h
@@ -21,6 +21,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@GOTOFF)\n"
+" ADD A1LbP,A1LbP,#LO(__dl_fini@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 29547cb34..257ca610f 100644
--- a/ldso/ldso/metag/dl-sysdep.h
+++ b/ldso/ldso/metag/dl-sysdep.h
@@ -36,14 +36,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 78434167b..e0f981741 100644
--- a/ldso/ldso/metag/elfinterp.c
+++ b/ldso/ldso/metag/elfinterp.c
@@ -43,43 +43,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);
}
@@ -102,28 +95,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);
@@ -141,7 +134,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",
@@ -161,8 +154,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;
@@ -170,30 +163,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__)
@@ -201,13 +199,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__)
@@ -215,11 +221,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:
@@ -227,13 +234,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);
@@ -244,8 +262,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;
@@ -253,8 +271,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;
@@ -291,9 +309,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 000000000..2f00a9fef
--- /dev/null
+++ b/ldso/ldso/metag/metag_load_tp.S
@@ -0,0 +1,20 @@
+! Copyright (C) 2013 Imagination Technologies Ltd.
+
+! Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+
+#include <features.h>
+
+#ifdef __UCLIBC_HAS_THREADS_NATIVE__
+
+#include <sysdep.h>
+
+ .text
+ .global ___metag_load_tp
+ .type ___metag_load_tp,@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 f7083a48b..dbf0b0fd4 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 0e6fbfe12..3f11d6da8 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 000000000..64aa50bc4
--- /dev/null
+++ b/libc/sysdeps/linux/metag/bits/atomic.h
@@ -0,0 +1,66 @@
+/*
+ * Copyrith (C) 2013 Imagination Technologies Ltd.
+ *
+ * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ *
+ */
+
+#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 b5c8fc58c..7ea09c2c3 100644
--- a/libc/sysdeps/linux/metag/bits/syscalls.h
+++ b/libc/sysdeps/linux/metag/bits/syscalls.h
@@ -49,8 +49,9 @@
(__extension__ \
({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) \
@@ -68,32 +69,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 8fff56710..d9d836338 100644
--- a/libc/sysdeps/linux/metag/clone.S
+++ b/libc/sysdeps/linux/metag/clone.S
@@ -8,7 +8,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@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
@@ -25,8 +35,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
@@ -38,14 +52,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