Skip to content

Commit

Permalink
x86/insn: Support big endian cross-compiles
Browse files Browse the repository at this point in the history
The x86 instruction decoder code is shared across the kernel source and
the tools. Currently objtool seems to be the only tool from build tools
needed which breaks x86 cross-compilation on big endian systems. Make
the x86 instruction decoder build host endianness agnostic to support
x86 cross-compilation and enable objtool to implement endianness
awareness for big endian architectures support.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Co-developed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
  • Loading branch information
Martin Schwidefsky authored and Josh Poimboeuf committed Jan 14, 2021
1 parent c8d7b7e commit 1d509f2
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 112 deletions.
33 changes: 33 additions & 0 deletions arch/x86/include/asm/insn.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
* Copyright (C) IBM Corporation, 2009
*/

#include <asm/byteorder.h>
/* insn_attr_t is defined in inat.h */
#include <asm/inat.h>

#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN)

struct insn_field {
union {
insn_value_t value;
Expand All @@ -20,6 +23,36 @@ struct insn_field {
unsigned char nbytes;
};

static inline void insn_field_set(struct insn_field *p, insn_value_t v,
unsigned char n)
{
p->value = v;
p->nbytes = n;
}

#else

struct insn_field {
insn_value_t value;
union {
insn_value_t little;
insn_byte_t bytes[4];
};
/* !0 if we've run insn_get_xxx() for this field */
unsigned char got;
unsigned char nbytes;
};

static inline void insn_field_set(struct insn_field *p, insn_value_t v,
unsigned char n)
{
p->value = v;
p->little = __cpu_to_le32(v);
p->nbytes = n;
}

#endif

struct insn {
struct insn_field prefixes; /*
* Prefixes
Expand Down
101 changes: 47 additions & 54 deletions arch/x86/lib/insn.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Copyright (C) IBM Corporation, 2002, 2004, 2009
*/

#include <linux/kernel.h>
#ifdef __KERNEL__
#include <linux/string.h>
#else
Expand All @@ -15,15 +16,28 @@

#include <asm/emulate_prefix.h>

#define leXX_to_cpu(t, r) \
({ \
__typeof__(t) v; \
switch (sizeof(t)) { \
case 4: v = le32_to_cpu(r); break; \
case 2: v = le16_to_cpu(r); break; \
case 1: v = r; break; \
default: \
BUILD_BUG(); break; \
} \
v; \
})

/* Verify next sizeof(t) bytes can be on the same instruction */
#define validate_next(t, insn, n) \
((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr)

#define __get_next(t, insn) \
({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; })
({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); leXX_to_cpu(t, r); })

#define __peek_nbyte_next(t, insn, n) \
({ t r = *(t*)((insn)->next_byte + n); r; })
({ t r = *(t*)((insn)->next_byte + n); leXX_to_cpu(t, r); })

#define get_next(t, insn) \
({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); })
Expand Down Expand Up @@ -157,8 +171,7 @@ void insn_get_prefixes(struct insn *insn)
b = peek_next(insn_byte_t, insn);
attr = inat_get_opcode_attribute(b);
if (inat_is_rex_prefix(attr)) {
insn->rex_prefix.value = b;
insn->rex_prefix.nbytes = 1;
insn_field_set(&insn->rex_prefix, b, 1);
insn->next_byte++;
if (X86_REX_W(b))
/* REX.W overrides opnd_size */
Expand Down Expand Up @@ -295,8 +308,7 @@ void insn_get_modrm(struct insn *insn)

if (inat_has_modrm(insn->attr)) {
mod = get_next(insn_byte_t, insn);
modrm->value = mod;
modrm->nbytes = 1;
insn_field_set(modrm, mod, 1);
if (inat_is_group(insn->attr)) {
pfx_id = insn_last_prefix_id(insn);
insn->attr = inat_get_group_attribute(mod, pfx_id,
Expand Down Expand Up @@ -334,7 +346,7 @@ int insn_rip_relative(struct insn *insn)
* For rip-relative instructions, the mod field (top 2 bits)
* is zero and the r/m field (bottom 3 bits) is 0x5.
*/
return (modrm->nbytes && (modrm->value & 0xc7) == 0x5);
return (modrm->nbytes && (modrm->bytes[0] & 0xc7) == 0x5);
}

/**
Expand All @@ -353,11 +365,11 @@ void insn_get_sib(struct insn *insn)
if (!insn->modrm.got)
insn_get_modrm(insn);
if (insn->modrm.nbytes) {
modrm = (insn_byte_t)insn->modrm.value;
modrm = insn->modrm.bytes[0];
if (insn->addr_bytes != 2 &&
X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) {
insn->sib.value = get_next(insn_byte_t, insn);
insn->sib.nbytes = 1;
insn_field_set(&insn->sib,
get_next(insn_byte_t, insn), 1);
}
}
insn->sib.got = 1;
Expand Down Expand Up @@ -407,19 +419,18 @@ void insn_get_displacement(struct insn *insn)
if (mod == 3)
goto out;
if (mod == 1) {
insn->displacement.value = get_next(signed char, insn);
insn->displacement.nbytes = 1;
insn_field_set(&insn->displacement,
get_next(signed char, insn), 1);
} else if (insn->addr_bytes == 2) {
if ((mod == 0 && rm == 6) || mod == 2) {
insn->displacement.value =
get_next(short, insn);
insn->displacement.nbytes = 2;
insn_field_set(&insn->displacement,
get_next(short, insn), 2);
}
} else {
if ((mod == 0 && rm == 5) || mod == 2 ||
(mod == 0 && base == 5)) {
insn->displacement.value = get_next(int, insn);
insn->displacement.nbytes = 4;
insn_field_set(&insn->displacement,
get_next(int, insn), 4);
}
}
}
Expand All @@ -435,18 +446,14 @@ static int __get_moffset(struct insn *insn)
{
switch (insn->addr_bytes) {
case 2:
insn->moffset1.value = get_next(short, insn);
insn->moffset1.nbytes = 2;
insn_field_set(&insn->moffset1, get_next(short, insn), 2);
break;
case 4:
insn->moffset1.value = get_next(int, insn);
insn->moffset1.nbytes = 4;
insn_field_set(&insn->moffset1, get_next(int, insn), 4);
break;
case 8:
insn->moffset1.value = get_next(int, insn);
insn->moffset1.nbytes = 4;
insn->moffset2.value = get_next(int, insn);
insn->moffset2.nbytes = 4;
insn_field_set(&insn->moffset1, get_next(int, insn), 4);
insn_field_set(&insn->moffset2, get_next(int, insn), 4);
break;
default: /* opnd_bytes must be modified manually */
goto err_out;
Expand All @@ -464,13 +471,11 @@ static int __get_immv32(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate.value = get_next(short, insn);
insn->immediate.nbytes = 2;
insn_field_set(&insn->immediate, get_next(short, insn), 2);
break;
case 4:
case 8:
insn->immediate.value = get_next(int, insn);
insn->immediate.nbytes = 4;
insn_field_set(&insn->immediate, get_next(int, insn), 4);
break;
default: /* opnd_bytes must be modified manually */
goto err_out;
Expand All @@ -487,18 +492,15 @@ static int __get_immv(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate1.value = get_next(short, insn);
insn->immediate1.nbytes = 2;
insn_field_set(&insn->immediate1, get_next(short, insn), 2);
break;
case 4:
insn->immediate1.value = get_next(int, insn);
insn_field_set(&insn->immediate1, get_next(int, insn), 4);
insn->immediate1.nbytes = 4;
break;
case 8:
insn->immediate1.value = get_next(int, insn);
insn->immediate1.nbytes = 4;
insn->immediate2.value = get_next(int, insn);
insn->immediate2.nbytes = 4;
insn_field_set(&insn->immediate1, get_next(int, insn), 4);
insn_field_set(&insn->immediate2, get_next(int, insn), 4);
break;
default: /* opnd_bytes must be modified manually */
goto err_out;
Expand All @@ -515,21 +517,18 @@ static int __get_immptr(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate1.value = get_next(short, insn);
insn->immediate1.nbytes = 2;
insn_field_set(&insn->immediate1, get_next(short, insn), 2);
break;
case 4:
insn->immediate1.value = get_next(int, insn);
insn->immediate1.nbytes = 4;
insn_field_set(&insn->immediate1, get_next(int, insn), 4);
break;
case 8:
/* ptr16:64 is not exist (no segment) */
return 0;
default: /* opnd_bytes must be modified manually */
goto err_out;
}
insn->immediate2.value = get_next(unsigned short, insn);
insn->immediate2.nbytes = 2;
insn_field_set(&insn->immediate2, get_next(unsigned short, insn), 2);
insn->immediate1.got = insn->immediate2.got = 1;

return 1;
Expand Down Expand Up @@ -565,22 +564,17 @@ void insn_get_immediate(struct insn *insn)

switch (inat_immediate_size(insn->attr)) {
case INAT_IMM_BYTE:
insn->immediate.value = get_next(signed char, insn);
insn->immediate.nbytes = 1;
insn_field_set(&insn->immediate, get_next(signed char, insn), 1);
break;
case INAT_IMM_WORD:
insn->immediate.value = get_next(short, insn);
insn->immediate.nbytes = 2;
insn_field_set(&insn->immediate, get_next(short, insn), 2);
break;
case INAT_IMM_DWORD:
insn->immediate.value = get_next(int, insn);
insn->immediate.nbytes = 4;
insn_field_set(&insn->immediate, get_next(int, insn), 4);
break;
case INAT_IMM_QWORD:
insn->immediate1.value = get_next(int, insn);
insn->immediate1.nbytes = 4;
insn->immediate2.value = get_next(int, insn);
insn->immediate2.nbytes = 4;
insn_field_set(&insn->immediate1, get_next(int, insn), 4);
insn_field_set(&insn->immediate2, get_next(int, insn), 4);
break;
case INAT_IMM_PTR:
if (!__get_immptr(insn))
Expand All @@ -599,8 +593,7 @@ void insn_get_immediate(struct insn *insn)
goto err_out;
}
if (inat_has_second_immediate(insn->attr)) {
insn->immediate2.value = get_next(signed char, insn);
insn->immediate2.nbytes = 1;
insn_field_set(&insn->immediate2, get_next(signed char, insn), 1);
}
done:
insn->immediate.got = 1;
Expand Down
4 changes: 0 additions & 4 deletions arch/x86/tools/insn_sanity.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define unlikely(cond) (cond)
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

#include <asm/insn.h>
#include <inat.c>
#include <insn.c>
Expand Down
33 changes: 33 additions & 0 deletions tools/arch/x86/include/asm/insn.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
* Copyright (C) IBM Corporation, 2009
*/

#include <asm/byteorder.h>
/* insn_attr_t is defined in inat.h */
#include "inat.h"

#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN)

struct insn_field {
union {
insn_value_t value;
Expand All @@ -20,6 +23,36 @@ struct insn_field {
unsigned char nbytes;
};

static inline void insn_field_set(struct insn_field *p, insn_value_t v,
unsigned char n)
{
p->value = v;
p->nbytes = n;
}

#else

struct insn_field {
insn_value_t value;
union {
insn_value_t little;
insn_byte_t bytes[4];
};
/* !0 if we've run insn_get_xxx() for this field */
unsigned char got;
unsigned char nbytes;
};

static inline void insn_field_set(struct insn_field *p, insn_value_t v,
unsigned char n)
{
p->value = v;
p->little = __cpu_to_le32(v);
p->nbytes = n;
}

#endif

struct insn {
struct insn_field prefixes; /*
* Prefixes
Expand Down
Loading

0 comments on commit 1d509f2

Please sign in to comment.