summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2009-08-24 16:19:47 +0000
committerbz <bz@FreeBSD.org>2009-08-24 16:19:47 +0000
commitba7b3afabc9c18fbc00ee2b3a04d75cc7bf6f923 (patch)
treeb21e68a77cf9b8fa18e8b538ce504aa2bd73af12
parentad8cfd197776d149ee819a30b8733efd86079774 (diff)
downloadFreeBSD-src-ba7b3afabc9c18fbc00ee2b3a04d75cc7bf6f923.zip
FreeBSD-src-ba7b3afabc9c18fbc00ee2b3a04d75cc7bf6f923.tar.gz
Fix handling of .note.ABI-tag section for GNU systems [1].
Handle GNU/Linux according to LSB Core Specification 4.0, Chapter 11. Object Format, 11.8. ABI note tag. Also check the first word of desc, not only name, according to glibc abi-tags specification to distinguish between Linux and kFreeBSD. Add explicit handling for Debian GNU/kFreeBSD, which runs on our kernels as well [2]. In {amd64,i386}/trap.c, when checking osrel of the current process, also check the ABI to not change the signal behaviour for Linux binary processes, now that we save an osrel version for all three from the lists above in struct proc [2]. These changes make it possible to run FreeBSD, Debian GNU/kFreeBSD and Linux binaries on the same machine again for at least i386 and amd64, and no longer break kFreeBSD which was detected as GNU(/Linux). PR: kern/135468 Submitted by: dchagin [1] (initial patch) Suggested by: kib [2] Tested by: Petr Salinger (Petr.Salinger seznam.cz) for kFreeBSD Reviewed by: kib MFC after: 3 days
-rw-r--r--sys/amd64/amd64/elf_machdep.c17
-rw-r--r--sys/amd64/amd64/trap.c4
-rw-r--r--sys/amd64/linux32/linux32_sysvec.c35
-rw-r--r--sys/compat/ia32/ia32_sysvec.c15
-rw-r--r--sys/i386/i386/elf_machdep.c16
-rw-r--r--sys/i386/i386/trap.c4
-rw-r--r--sys/i386/linux/linux_sysvec.c35
-rw-r--r--sys/kern/imgact_elf.c60
-rw-r--r--sys/sys/imgact_elf.h6
9 files changed, 173 insertions, 19 deletions
diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c
index c5e19cf..ea48b25 100644
--- a/sys/amd64/amd64/elf_machdep.c
+++ b/sys/amd64/amd64/elf_machdep.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
+#include <sys/sysent.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
@@ -108,6 +109,22 @@ SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY,
(sysinit_cfunc_t) elf64_insert_brand_entry,
&freebsd_brand_oinfo);
+static Elf64_Brandinfo kfreebsd_brand_info = {
+ .brand = ELFOSABI_FREEBSD,
+ .machine = EM_X86_64,
+ .compat_3_brand = "FreeBSD",
+ .emul_path = NULL,
+ .interp_path = "/lib/ld-kfreebsd-x86-64.so.1",
+ .sysvec = &elf64_freebsd_sysvec,
+ .interp_newpath = NULL,
+ .brand_note = &elf64_kfreebsd_brandnote,
+ .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE
+};
+
+SYSINIT(kelf64, SI_SUB_EXEC, SI_ORDER_ANY,
+ (sysinit_cfunc_t) elf64_insert_brand_entry,
+ &kfreebsd_brand_info);
+
void
elf64_dump_thread(struct thread *td __unused, void *dst __unused,
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
index 323e8d1..65f761e 100644
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/trap.c
@@ -409,7 +409,9 @@ trap(struct trapframe *frame)
* This check also covers the images
* without the ABI-tag ELF note.
*/
- if (p->p_osrel >= 700004) {
+ if (SV_CURPROC_ABI() ==
+ SV_ABI_FREEBSD &&
+ p->p_osrel >= 700004) {
i = SIGSEGV;
ucode = SEGV_ACCERR;
} else {
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index 77186a1..54a04ee 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -127,6 +127,7 @@ static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
static void exec_linux_setregs(struct thread *td, u_long entry,
u_long stack, u_long ps_strings);
static void linux32_fixlimit(struct rlimit *rl, int which);
+static boolean_t linux32_trans_osrel(const Elf_Note *note, int32_t *osrel);
static eventhandler_tag linux_exit_tag;
static eventhandler_tag linux_schedtail_tag;
@@ -1066,14 +1067,38 @@ struct sysentvec elf_linux_sysvec = {
.sv_flags = SV_ABI_LINUX | SV_ILP32 | SV_IA32
};
-static char GNULINUX_ABI_VENDOR[] = "GNU";
+static char GNU_ABI_VENDOR[] = "GNU";
+static int GNULINUX_ABI_DESC = 0;
+
+static boolean_t
+linux32_trans_osrel(const Elf_Note *note, int32_t *osrel)
+{
+ const Elf32_Word *desc;
+ uintptr_t p;
+
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+
+ desc = (const Elf32_Word *)p;
+ if (desc[0] != GNULINUX_ABI_DESC)
+ return (FALSE);
+
+ /*
+ * For linux we encode osrel as follows (see linux_mib.c):
+ * VVVMMMIII (version, major, minor), see linux_mib.c.
+ */
+ *osrel = desc[1] * 1000000 + desc[2] * 1000 + desc[3];
+
+ return (TRUE);
+}
static Elf_Brandnote linux32_brandnote = {
- .hdr.n_namesz = sizeof(GNULINUX_ABI_VENDOR),
- .hdr.n_descsz = 16,
+ .hdr.n_namesz = sizeof(GNU_ABI_VENDOR),
+ .hdr.n_descsz = 16, /* XXX at least 16 */
.hdr.n_type = 1,
- .vendor = GNULINUX_ABI_VENDOR,
- .flags = 0
+ .vendor = GNU_ABI_VENDOR,
+ .flags = BN_TRANSLATE_OSREL,
+ .trans_osrel = linux32_trans_osrel
};
static Elf32_Brandinfo linux_brand = {
diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c
index af8168e..5c2c571 100644
--- a/sys/compat/ia32/ia32_sysvec.c
+++ b/sys/compat/ia32/ia32_sysvec.c
@@ -172,6 +172,21 @@ SYSINIT(oia32, SI_SUB_EXEC, SI_ORDER_ANY,
(sysinit_cfunc_t) elf32_insert_brand_entry,
&ia32_brand_oinfo);
+static Elf32_Brandinfo kia32_brand_info = {
+ .brand = ELFOSABI_FREEBSD,
+ .machine = EM_386,
+ .compat_3_brand = "FreeBSD",
+ .emul_path = NULL,
+ .interp_path = "/lib/ld.so.1",
+ .sysvec = &ia32_freebsd_sysvec,
+ .brand_note = &elf32_kfreebsd_brandnote,
+ .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE
+};
+
+SYSINIT(kia32, SI_SUB_EXEC, SI_ORDER_ANY,
+ (sysinit_cfunc_t) elf32_insert_brand_entry,
+ &kia32_brand_info);
+
void
elf32_dump_thread(struct thread *td __unused, void *dst __unused,
diff --git a/sys/i386/i386/elf_machdep.c b/sys/i386/i386/elf_machdep.c
index b68a73e..a4ff9e2 100644
--- a/sys/i386/i386/elf_machdep.c
+++ b/sys/i386/i386/elf_machdep.c
@@ -108,6 +108,22 @@ SYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY,
(sysinit_cfunc_t) elf32_insert_brand_entry,
&freebsd_brand_oinfo);
+static Elf32_Brandinfo kfreebsd_brand_info = {
+ .brand = ELFOSABI_FREEBSD,
+ .machine = EM_386,
+ .compat_3_brand = "FreeBSD",
+ .emul_path = NULL,
+ .interp_path = "/lib/ld.so.1",
+ .sysvec = &elf32_freebsd_sysvec,
+ .interp_newpath = NULL,
+ .brand_note = &elf32_kfreebsd_brandnote,
+ .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE
+};
+
+SYSINIT(kelf32, SI_SUB_EXEC, SI_ORDER_ANY,
+ (sysinit_cfunc_t) elf32_insert_brand_entry,
+ &kfreebsd_brand_info);
+
void
elf32_dump_thread(struct thread *td __unused, void *dst __unused,
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index 354d791..f7064f0 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/trap.c
@@ -423,7 +423,9 @@ trap(struct trapframe *frame)
* This check also covers the images
* without the ABI-tag ELF note.
*/
- if (p->p_osrel >= 700004) {
+ if (SV_CURPROC_ABI() ==
+ SV_ABI_FREEBSD &&
+ p->p_osrel >= 700004) {
i = SIGSEGV;
ucode = SEGV_ACCERR;
} else {
diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c
index 186e14c..d07f655 100644
--- a/sys/i386/linux/linux_sysvec.c
+++ b/sys/i386/linux/linux_sysvec.c
@@ -108,6 +108,7 @@ static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
static void exec_linux_setregs(struct thread *td, u_long entry,
u_long stack, u_long ps_strings);
static register_t *linux_copyout_strings(struct image_params *imgp);
+static boolean_t linux_trans_osrel(const Elf_Note *note, int32_t *osrel);
static int linux_szplatform;
const char *linux_platform;
@@ -1027,14 +1028,38 @@ struct sysentvec elf_linux_sysvec = {
.sv_flags = SV_ABI_LINUX | SV_IA32 | SV_ILP32
};
-static char GNULINUX_ABI_VENDOR[] = "GNU";
+static char GNU_ABI_VENDOR[] = "GNU";
+static int GNULINUX_ABI_DESC = 0;
+
+static boolean_t
+linux_trans_osrel(const Elf_Note *note, int32_t *osrel)
+{
+ const Elf32_Word *desc;
+ uintptr_t p;
+
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+
+ desc = (const Elf32_Word *)p;
+ if (desc[0] != GNULINUX_ABI_DESC)
+ return (FALSE);
+
+ /*
+ * For linux we encode osrel as follows (see linux_mib.c):
+ * VVVMMMIII (version, major, minor), see linux_mib.c.
+ */
+ *osrel = desc[1] * 1000000 + desc[2] * 1000 + desc[3];
+
+ return (TRUE);
+}
static Elf_Brandnote linux_brandnote = {
- .hdr.n_namesz = sizeof(GNULINUX_ABI_VENDOR),
- .hdr.n_descsz = 16,
+ .hdr.n_namesz = sizeof(GNU_ABI_VENDOR),
+ .hdr.n_descsz = 16, /* XXX at least 16 */
.hdr.n_type = 1,
- .vendor = GNULINUX_ABI_VENDOR,
- .flags = 0
+ .vendor = GNU_ABI_VENDOR,
+ .flags = BN_TRANSLATE_OSREL,
+ .trans_osrel = linux_trans_osrel
};
static Elf32_Brandinfo linux_brand = {
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index e2c0a12..6803523 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -86,6 +86,9 @@ static int __elfN(load_section)(struct vmspace *vmspace, vm_object_t object,
vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz,
vm_prot_t prot, size_t pagesize);
static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp);
+static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note,
+ int32_t *osrel);
+static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel);
static boolean_t __elfN(check_note)(struct image_params *imgp,
Elf_Brandnote *checknote, int32_t *osrel);
@@ -116,9 +119,56 @@ Elf_Brandnote __elfN(freebsd_brandnote) = {
.hdr.n_descsz = sizeof(int32_t),
.hdr.n_type = 1,
.vendor = FREEBSD_ABI_VENDOR,
- .flags = BN_CAN_FETCH_OSREL
+ .flags = BN_TRANSLATE_OSREL,
+ .trans_osrel = __elfN(freebsd_trans_osrel)
};
+static boolean_t
+__elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel)
+{
+ uintptr_t p;
+
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+ *osrel = *(const int32_t *)(p);
+
+ return (TRUE);
+}
+
+static const char GNU_ABI_VENDOR[] = "GNU";
+static int GNU_KFREEBSD_ABI_DESC = 3;
+
+Elf_Brandnote __elfN(kfreebsd_brandnote) = {
+ .hdr.n_namesz = sizeof(GNU_ABI_VENDOR),
+ .hdr.n_descsz = 16, /* XXX at least 16 */
+ .hdr.n_type = 1,
+ .vendor = GNU_ABI_VENDOR,
+ .flags = BN_TRANSLATE_OSREL,
+ .trans_osrel = kfreebsd_trans_osrel
+};
+
+static boolean_t
+kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel)
+{
+ const Elf32_Word *desc;
+ uintptr_t p;
+
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+
+ desc = (const Elf32_Word *)p;
+ if (desc[0] != GNU_KFREEBSD_ABI_DESC)
+ return (FALSE);
+
+ /*
+ * Debian GNU/kFreeBSD embed the earliest compatible kernel version
+ * (__FreeBSD_version: <major><two digit minor>Rxx) in the LSB way.
+ */
+ *osrel = desc[1] * 100000 + desc[2] * 1000 + desc[3];
+
+ return (TRUE);
+}
+
int
__elfN(insert_brand_entry)(Elf_Brandinfo *entry)
{
@@ -1371,11 +1421,9 @@ __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote,
* Fetch the osreldate for binary
* from the ELF OSABI-note if necessary.
*/
- if ((checknote->flags & BN_CAN_FETCH_OSREL) != 0 &&
- osrel != NULL)
- *osrel = *(const int32_t *) (note_name +
- roundup2(checknote->hdr.n_namesz,
- sizeof(Elf32_Addr)));
+ if ((checknote->flags & BN_TRANSLATE_OSREL) != 0 &&
+ checknote->trans_osrel != NULL)
+ return (checknote->trans_osrel(note, osrel));
return (TRUE);
nextnote:
diff --git a/sys/sys/imgact_elf.h b/sys/sys/imgact_elf.h
index 60979d9..3eecf85 100644
--- a/sys/sys/imgact_elf.h
+++ b/sys/sys/imgact_elf.h
@@ -58,7 +58,10 @@ typedef struct {
Elf_Note hdr;
const char * vendor;
int flags;
-#define BN_CAN_FETCH_OSREL 0x0001
+ boolean_t (*trans_osrel)(const Elf_Note *, int32_t *);
+#define BN_CAN_FETCH_OSREL 0x0001 /* Deprecated. */
+#define BN_TRANSLATE_OSREL 0x0002 /* Use trans_osrel fetch osrel after */
+ /* checking ABI contraint if needed. */
} Elf_Brandnote;
typedef struct {
@@ -91,6 +94,7 @@ void __elfN(dump_thread)(struct thread *, void *, size_t *);
extern int __elfN(fallback_brand);
extern Elf_Brandnote __elfN(freebsd_brandnote);
+extern Elf_Brandnote __elfN(kfreebsd_brandnote);
#endif /* _KERNEL */
#endif /* !_SYS_IMGACT_ELF_H_ */
OpenPOWER on IntegriCloud