2024-03-11 22:23:46 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <string.h>
|
|
|
|
#include <objtool/check.h>
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
#include <objtool/warn.h>
|
|
|
|
#include <asm/inst.h>
|
2024-03-11 22:23:47 +08:00
|
|
|
#include <asm/orc_types.h>
|
|
|
|
#include <linux/objtool_types.h>
|
objtool: Handle PC relative relocation type
For the most part, an absolute relocation type is used for rodata.
In the case of STT_SECTION, reloc->sym->offset is always zero, for
the other symbol types, reloc_addend(reloc) is always zero, thus it
can use a simple statement "reloc->sym->offset + reloc_addend(reloc)"
to obtain the symbol offset for various symbol types.
When compiling on LoongArch, there exist PC relative relocation types
for rodata, it needs to calculate the symbol offset with "S + A - PC"
according to the spec of "ELF for the LoongArch Architecture".
If there is only one jump table in the rodata, the "PC" is the entry
address which is equal with the value of reloc_offset(reloc), at this
time, reloc_offset(table) is 0.
If there are many jump tables in the rodata, the "PC" is the offset
of the jump table's base address which is equal with the value of
reloc_offset(reloc) - reloc_offset(table).
So for LoongArch, if the relocation type is PC relative, it can use a
statement "reloc_offset(reloc) - reloc_offset(table)" to get the "PC"
value when calculating the symbol offset with "S + A - PC" for one or
many jump tables in the rodata.
Add an arch-specific function arch_jump_table_sym_offset() to assign
the symbol offset, for the most part that is an absolute relocation,
the default value is "reloc->sym->offset + reloc_addend(reloc)" in
the weak definition, it can be overridden by each architecture that
has different requirements.
Link: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Link: https://lore.kernel.org/r/20250211115016.26913-4-yangtiezhu@loongson.cn
Acked-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
2025-02-11 19:50:12 +08:00
|
|
|
#include <arch/elf.h>
|
2024-03-11 22:23:46 +08:00
|
|
|
|
|
|
|
int arch_ftrace_match(char *name)
|
|
|
|
{
|
|
|
|
return !strcmp(name, "_mcount");
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long arch_jump_destination(struct instruction *insn)
|
|
|
|
{
|
|
|
|
return insn->offset + (insn->immediate << 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long arch_dest_reloc_offset(int addend)
|
|
|
|
{
|
|
|
|
return addend;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool arch_pc_relative_reloc(struct reloc *reloc)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool arch_callee_saved_reg(unsigned char reg)
|
|
|
|
{
|
|
|
|
switch (reg) {
|
|
|
|
case CFI_RA:
|
|
|
|
case CFI_FP:
|
|
|
|
case CFI_S0 ... CFI_S8:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int arch_decode_hint_reg(u8 sp_reg, int *base)
|
|
|
|
{
|
2024-03-11 22:23:47 +08:00
|
|
|
switch (sp_reg) {
|
|
|
|
case ORC_REG_UNDEFINED:
|
|
|
|
*base = CFI_UNDEFINED;
|
|
|
|
break;
|
|
|
|
case ORC_REG_SP:
|
|
|
|
*base = CFI_SP;
|
|
|
|
break;
|
|
|
|
case ORC_REG_FP:
|
|
|
|
*base = CFI_FP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:23:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
static bool is_loongarch(const struct elf *elf)
|
|
|
|
{
|
|
|
|
if (elf->ehdr.e_machine == EM_LOONGARCH)
|
|
|
|
return true;
|
|
|
|
|
2025-03-31 21:26:41 -07:00
|
|
|
ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ADD_OP(op) \
|
|
|
|
if (!(op = calloc(1, sizeof(*op)))) \
|
|
|
|
return -1; \
|
|
|
|
else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
|
|
|
|
|
|
|
|
static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
switch (inst.reg0i26_format.opcode) {
|
|
|
|
case b_op:
|
|
|
|
insn->type = INSN_JUMP_UNCONDITIONAL;
|
|
|
|
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
|
|
|
|
inst.reg0i26_format.immediate_l, 25);
|
|
|
|
break;
|
|
|
|
case bl_op:
|
|
|
|
insn->type = INSN_CALL;
|
|
|
|
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
|
|
|
|
inst.reg0i26_format.immediate_l, 25);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
switch (inst.reg1i21_format.opcode) {
|
|
|
|
case beqz_op:
|
|
|
|
case bnez_op:
|
|
|
|
case bceqz_op:
|
|
|
|
insn->type = INSN_JUMP_CONDITIONAL;
|
|
|
|
insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
|
|
|
|
inst.reg1i21_format.immediate_l, 20);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,
|
|
|
|
struct instruction *insn,
|
|
|
|
struct stack_op **ops_list,
|
|
|
|
struct stack_op *op)
|
|
|
|
{
|
|
|
|
switch (inst.reg2i12_format.opcode) {
|
|
|
|
case addid_op:
|
|
|
|
if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {
|
objtool: Handle frame pointer related instructions
After commit a0f7085f6a63 ("LoongArch: Add RANDOMIZE_KSTACK_OFFSET
support"), there are three new instructions "addi.d $fp, $sp, 32",
"sub.d $sp, $sp, $t0" and "addi.d $sp, $fp, -32" for the secondary
stack in do_syscall(), then there is a objtool warning "return with
modified stack frame" and no handle_syscall() which is the previous
frame of do_syscall() in the call trace when executing the command
"echo l > /proc/sysrq-trigger".
objdump shows something like this:
0000000000000000 <do_syscall>:
0: 02ff8063 addi.d $sp, $sp, -32
4: 29c04076 st.d $fp, $sp, 16
8: 29c02077 st.d $s0, $sp, 8
c: 29c06061 st.d $ra, $sp, 24
10: 02c08076 addi.d $fp, $sp, 32
...
74: 0011b063 sub.d $sp, $sp, $t0
...
a8: 4c000181 jirl $ra, $t0, 0
...
dc: 02ff82c3 addi.d $sp, $fp, -32
e0: 28c06061 ld.d $ra, $sp, 24
e4: 28c04076 ld.d $fp, $sp, 16
e8: 28c02077 ld.d $s0, $sp, 8
ec: 02c08063 addi.d $sp, $sp, 32
f0: 4c000020 jirl $zero, $ra, 0
The instruction "sub.d $sp, $sp, $t0" changes the stack bottom and the
new stack size is a random value, in order to find the return address of
do_syscall() which is stored in the original stack frame after executing
"jirl $ra, $t0, 0", it should use fp which points to the original stack
top.
At the beginning, the thought is tended to decode the secondary stack
instruction "sub.d $sp, $sp, $t0" and set it as a label, then check this
label for the two frame pointer instructions to change the cfa base and
cfa offset during the period of secondary stack in update_cfi_state().
This is valid for GCC but invalid for Clang due to there are different
secondary stack instructions for ClangBuiltLinux on LoongArch, something
like this:
0000000000000000 <do_syscall>:
...
88: 00119064 sub.d $a0, $sp, $a0
8c: 00150083 or $sp, $a0, $zero
...
Actually, it equals to a single instruction "sub.d $sp, $sp, $a0", but
there is no proper condition to check it as a label like GCC, and so the
beginning thought is not a good way.
Essentially, there are two special frame pointer instructions which are
"addi.d $fp, $sp, imm" and "addi.d $sp, $fp, imm", the first one points
fp to the original stack top and the second one restores the original
stack bottom from fp.
Based on the above analysis, in order to avoid adding an arch-specific
update_cfi_state(), we just add a member "frame_pointer" in the "struct
symbol" as a label to avoid affecting the current normal case, then set
it as true only if there is "addi.d $sp, $fp, imm". The last is to check
this label for the two frame pointer instructions to change the cfa base
and cfa offset in update_cfi_state().
Tested with the following two configs:
(1) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=n
(2) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
By the way, there is no effect for x86 with this patch, tested on the
x86 machine with Fedora 40 system.
Cc: stable@vger.kernel.org # 6.9+
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-09-17 22:23:09 +08:00
|
|
|
/* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
|
|
|
ADD_OP(op) {
|
|
|
|
op->src.type = OP_SRC_ADD;
|
|
|
|
op->src.reg = inst.reg2i12_format.rj;
|
|
|
|
op->src.offset = insn->immediate;
|
|
|
|
op->dest.type = OP_DEST_REG;
|
|
|
|
op->dest.reg = inst.reg2i12_format.rd;
|
|
|
|
}
|
|
|
|
}
|
objtool: Handle frame pointer related instructions
After commit a0f7085f6a63 ("LoongArch: Add RANDOMIZE_KSTACK_OFFSET
support"), there are three new instructions "addi.d $fp, $sp, 32",
"sub.d $sp, $sp, $t0" and "addi.d $sp, $fp, -32" for the secondary
stack in do_syscall(), then there is a objtool warning "return with
modified stack frame" and no handle_syscall() which is the previous
frame of do_syscall() in the call trace when executing the command
"echo l > /proc/sysrq-trigger".
objdump shows something like this:
0000000000000000 <do_syscall>:
0: 02ff8063 addi.d $sp, $sp, -32
4: 29c04076 st.d $fp, $sp, 16
8: 29c02077 st.d $s0, $sp, 8
c: 29c06061 st.d $ra, $sp, 24
10: 02c08076 addi.d $fp, $sp, 32
...
74: 0011b063 sub.d $sp, $sp, $t0
...
a8: 4c000181 jirl $ra, $t0, 0
...
dc: 02ff82c3 addi.d $sp, $fp, -32
e0: 28c06061 ld.d $ra, $sp, 24
e4: 28c04076 ld.d $fp, $sp, 16
e8: 28c02077 ld.d $s0, $sp, 8
ec: 02c08063 addi.d $sp, $sp, 32
f0: 4c000020 jirl $zero, $ra, 0
The instruction "sub.d $sp, $sp, $t0" changes the stack bottom and the
new stack size is a random value, in order to find the return address of
do_syscall() which is stored in the original stack frame after executing
"jirl $ra, $t0, 0", it should use fp which points to the original stack
top.
At the beginning, the thought is tended to decode the secondary stack
instruction "sub.d $sp, $sp, $t0" and set it as a label, then check this
label for the two frame pointer instructions to change the cfa base and
cfa offset during the period of secondary stack in update_cfi_state().
This is valid for GCC but invalid for Clang due to there are different
secondary stack instructions for ClangBuiltLinux on LoongArch, something
like this:
0000000000000000 <do_syscall>:
...
88: 00119064 sub.d $a0, $sp, $a0
8c: 00150083 or $sp, $a0, $zero
...
Actually, it equals to a single instruction "sub.d $sp, $sp, $a0", but
there is no proper condition to check it as a label like GCC, and so the
beginning thought is not a good way.
Essentially, there are two special frame pointer instructions which are
"addi.d $fp, $sp, imm" and "addi.d $sp, $fp, imm", the first one points
fp to the original stack top and the second one restores the original
stack bottom from fp.
Based on the above analysis, in order to avoid adding an arch-specific
update_cfi_state(), we just add a member "frame_pointer" in the "struct
symbol" as a label to avoid affecting the current normal case, then set
it as true only if there is "addi.d $sp, $fp, imm". The last is to check
this label for the two frame pointer instructions to change the cfa base
and cfa offset in update_cfi_state().
Tested with the following two configs:
(1) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=n
(2) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
By the way, there is no effect for x86 with this patch, tested on the
x86 machine with Fedora 40 system.
Cc: stable@vger.kernel.org # 6.9+
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-09-17 22:23:09 +08:00
|
|
|
if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) {
|
|
|
|
/* addi.d sp,fp,si12 */
|
|
|
|
struct symbol *func = find_func_containing(insn->sec, insn->offset);
|
|
|
|
|
|
|
|
if (!func)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
func->frame_pointer = true;
|
|
|
|
}
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
break;
|
|
|
|
case ldd_op:
|
|
|
|
if (inst.reg2i12_format.rj == CFI_SP) {
|
|
|
|
/* ld.d rd,sp,si12 */
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
|
|
|
ADD_OP(op) {
|
|
|
|
op->src.type = OP_SRC_REG_INDIRECT;
|
|
|
|
op->src.reg = CFI_SP;
|
|
|
|
op->src.offset = insn->immediate;
|
|
|
|
op->dest.type = OP_DEST_REG;
|
|
|
|
op->dest.reg = inst.reg2i12_format.rd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case std_op:
|
|
|
|
if (inst.reg2i12_format.rj == CFI_SP) {
|
|
|
|
/* st.d rd,sp,si12 */
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
|
|
|
ADD_OP(op) {
|
|
|
|
op->src.type = OP_SRC_REG;
|
|
|
|
op->src.reg = inst.reg2i12_format.rd;
|
|
|
|
op->dest.type = OP_DEST_REG_INDIRECT;
|
|
|
|
op->dest.reg = CFI_SP;
|
|
|
|
op->dest.offset = insn->immediate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case andi_op:
|
|
|
|
if (inst.reg2i12_format.rd == 0 &&
|
|
|
|
inst.reg2i12_format.rj == 0 &&
|
|
|
|
inst.reg2i12_format.immediate == 0)
|
|
|
|
/* andi r0,r0,0 */
|
|
|
|
insn->type = INSN_NOP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,
|
|
|
|
struct instruction *insn,
|
|
|
|
struct stack_op **ops_list,
|
|
|
|
struct stack_op *op)
|
|
|
|
{
|
|
|
|
switch (inst.reg2i14_format.opcode) {
|
|
|
|
case ldptrd_op:
|
|
|
|
if (inst.reg2i14_format.rj == CFI_SP) {
|
|
|
|
/* ldptr.d rd,sp,si14 */
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
|
|
|
|
ADD_OP(op) {
|
|
|
|
op->src.type = OP_SRC_REG_INDIRECT;
|
|
|
|
op->src.reg = CFI_SP;
|
|
|
|
op->src.offset = insn->immediate;
|
|
|
|
op->dest.type = OP_DEST_REG;
|
|
|
|
op->dest.reg = inst.reg2i14_format.rd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case stptrd_op:
|
|
|
|
if (inst.reg2i14_format.rj == CFI_SP) {
|
|
|
|
/* stptr.d ra,sp,0 */
|
|
|
|
if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&
|
|
|
|
inst.reg2i14_format.immediate == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* stptr.d rd,sp,si14 */
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
|
|
|
|
ADD_OP(op) {
|
|
|
|
op->src.type = OP_SRC_REG;
|
|
|
|
op->src.reg = inst.reg2i14_format.rd;
|
|
|
|
op->dest.type = OP_DEST_REG_INDIRECT;
|
|
|
|
op->dest.reg = CFI_SP;
|
|
|
|
op->dest.offset = insn->immediate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
switch (inst.reg2i16_format.opcode) {
|
|
|
|
case jirl_op:
|
|
|
|
if (inst.reg2i16_format.rd == 0 &&
|
|
|
|
inst.reg2i16_format.rj == CFI_RA &&
|
|
|
|
inst.reg2i16_format.immediate == 0) {
|
|
|
|
/* jirl r0,ra,0 */
|
|
|
|
insn->type = INSN_RETURN;
|
|
|
|
} else if (inst.reg2i16_format.rd == CFI_RA) {
|
|
|
|
/* jirl ra,rj,offs16 */
|
|
|
|
insn->type = INSN_CALL_DYNAMIC;
|
|
|
|
} else if (inst.reg2i16_format.rd == CFI_A0 &&
|
|
|
|
inst.reg2i16_format.immediate == 0) {
|
|
|
|
/*
|
|
|
|
* jirl a0,t0,0
|
|
|
|
* this is a special case in loongarch_suspend_enter,
|
|
|
|
* just treat it as a call instruction.
|
|
|
|
*/
|
|
|
|
insn->type = INSN_CALL_DYNAMIC;
|
|
|
|
} else if (inst.reg2i16_format.rd == 0 &&
|
|
|
|
inst.reg2i16_format.immediate == 0) {
|
|
|
|
/* jirl r0,rj,0 */
|
|
|
|
insn->type = INSN_JUMP_DYNAMIC;
|
|
|
|
} else if (inst.reg2i16_format.rd == 0 &&
|
|
|
|
inst.reg2i16_format.immediate != 0) {
|
|
|
|
/*
|
|
|
|
* jirl r0,t0,12
|
|
|
|
* this is a rare case in JUMP_VIRT_ADDR,
|
|
|
|
* just ignore it due to it is harmless for tracing.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/* jirl rd,rj,offs16 */
|
|
|
|
insn->type = INSN_JUMP_UNCONDITIONAL;
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case beq_op:
|
|
|
|
case bne_op:
|
|
|
|
case blt_op:
|
|
|
|
case bge_op:
|
|
|
|
case bltu_op:
|
|
|
|
case bgeu_op:
|
|
|
|
insn->type = INSN_JUMP_CONDITIONAL;
|
|
|
|
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:23:46 +08:00
|
|
|
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
|
|
|
|
unsigned long offset, unsigned int maxlen,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
struct stack_op **ops_list = &insn->stack_ops;
|
|
|
|
const struct elf *elf = file->elf;
|
|
|
|
struct stack_op *op = NULL;
|
|
|
|
union loongarch_instruction inst;
|
|
|
|
|
|
|
|
if (!is_loongarch(elf))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (maxlen < LOONGARCH_INSN_SIZE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
insn->len = LOONGARCH_INSN_SIZE;
|
|
|
|
insn->type = INSN_OTHER;
|
|
|
|
insn->immediate = 0;
|
|
|
|
|
|
|
|
inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
|
|
|
|
|
|
|
|
if (decode_insn_reg0i26_fomat(inst, insn))
|
|
|
|
return 0;
|
|
|
|
if (decode_insn_reg1i21_fomat(inst, insn))
|
|
|
|
return 0;
|
|
|
|
if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
|
|
|
|
return 0;
|
|
|
|
if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
|
|
|
|
return 0;
|
|
|
|
if (decode_insn_reg2i16_fomat(inst, insn))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (inst.word == 0)
|
|
|
|
insn->type = INSN_NOP;
|
|
|
|
else if (inst.reg0i15_format.opcode == break_op) {
|
|
|
|
/* break */
|
|
|
|
insn->type = INSN_BUG;
|
|
|
|
} else if (inst.reg2_format.opcode == ertn_op) {
|
|
|
|
/* ertn */
|
|
|
|
insn->type = INSN_RETURN;
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:23:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *arch_nop_insn(int len)
|
|
|
|
{
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
static u32 nop;
|
|
|
|
|
2025-03-31 21:26:41 -07:00
|
|
|
if (len != LOONGARCH_INSN_SIZE) {
|
|
|
|
ERROR("invalid NOP size: %d\n", len);
|
|
|
|
return NULL;
|
|
|
|
}
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
|
|
|
|
nop = LOONGARCH_INSN_NOP;
|
|
|
|
|
|
|
|
return (const char *)&nop;
|
2024-03-11 22:23:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *arch_ret_insn(int len)
|
|
|
|
{
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
static u32 ret;
|
|
|
|
|
2025-03-31 21:26:41 -07:00
|
|
|
if (len != LOONGARCH_INSN_SIZE) {
|
|
|
|
ERROR("invalid RET size: %d\n", len);
|
|
|
|
return NULL;
|
|
|
|
}
|
objtool/LoongArch: Implement instruction decoder
Only copy the minimal definitions of instruction opcodes and formats
in inst.h from arch/loongarch to tools/arch/loongarch, and also copy
the definition of sign_extend64() to tools/include/linux/bitops.h to
decode the following kinds of instructions:
(1) stack pointer related instructions
addi.d, ld.d, st.d, ldptr.d and stptr.d
(2) branch and jump related instructions
beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl
(3) other instructions
break, nop and ertn
See more info about instructions in LoongArch Reference Manual:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Co-developed-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
2024-03-11 22:23:47 +08:00
|
|
|
|
|
|
|
emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
|
|
|
|
|
|
|
|
return (const char *)&ret;
|
2024-03-11 22:23:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CFI_NUM_REGS; i++) {
|
|
|
|
state->regs[i].base = CFI_UNDEFINED;
|
|
|
|
state->regs[i].offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initial CFA (call frame address) */
|
|
|
|
state->cfa.base = CFI_SP;
|
|
|
|
state->cfa.offset = 0;
|
|
|
|
}
|
2025-02-11 19:50:11 +08:00
|
|
|
|
|
|
|
unsigned int arch_reloc_size(struct reloc *reloc)
|
|
|
|
{
|
|
|
|
switch (reloc_type(reloc)) {
|
|
|
|
case R_LARCH_32:
|
|
|
|
case R_LARCH_32_PCREL:
|
|
|
|
return 4;
|
|
|
|
default:
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
}
|
objtool: Handle PC relative relocation type
For the most part, an absolute relocation type is used for rodata.
In the case of STT_SECTION, reloc->sym->offset is always zero, for
the other symbol types, reloc_addend(reloc) is always zero, thus it
can use a simple statement "reloc->sym->offset + reloc_addend(reloc)"
to obtain the symbol offset for various symbol types.
When compiling on LoongArch, there exist PC relative relocation types
for rodata, it needs to calculate the symbol offset with "S + A - PC"
according to the spec of "ELF for the LoongArch Architecture".
If there is only one jump table in the rodata, the "PC" is the entry
address which is equal with the value of reloc_offset(reloc), at this
time, reloc_offset(table) is 0.
If there are many jump tables in the rodata, the "PC" is the offset
of the jump table's base address which is equal with the value of
reloc_offset(reloc) - reloc_offset(table).
So for LoongArch, if the relocation type is PC relative, it can use a
statement "reloc_offset(reloc) - reloc_offset(table)" to get the "PC"
value when calculating the symbol offset with "S + A - PC" for one or
many jump tables in the rodata.
Add an arch-specific function arch_jump_table_sym_offset() to assign
the symbol offset, for the most part that is an absolute relocation,
the default value is "reloc->sym->offset + reloc_addend(reloc)" in
the weak definition, it can be overridden by each architecture that
has different requirements.
Link: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Link: https://lore.kernel.org/r/20250211115016.26913-4-yangtiezhu@loongson.cn
Acked-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
2025-02-11 19:50:12 +08:00
|
|
|
|
|
|
|
unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
|
|
|
|
{
|
|
|
|
switch (reloc_type(reloc)) {
|
|
|
|
case R_LARCH_32_PCREL:
|
|
|
|
case R_LARCH_64_PCREL:
|
|
|
|
return reloc->sym->offset + reloc_addend(reloc) -
|
|
|
|
(reloc_offset(reloc) - reloc_offset(table));
|
|
|
|
default:
|
|
|
|
return reloc->sym->offset + reloc_addend(reloc);
|
|
|
|
}
|
|
|
|
}
|