Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RISC-V: Lifting bug in JALR rd, rs1, imm when rd == rs1 #6003

Closed
jeanmicheldeva opened this issue Oct 17, 2024 · 5 comments
Closed

RISC-V: Lifting bug in JALR rd, rs1, imm when rd == rs1 #6003

jeanmicheldeva opened this issue Oct 17, 2024 · 5 comments
Assignees
Labels
Arch: RISC-V Issues with the RISC-V architecture plugin Component: Architecture Issue needs changes to an architecture plugin Effort: Low Issue should take < 1 week Impact: Low Issue is a papercut or has a good, supported workaround Type: Bug Issue is a non-crashing bug with repro steps
Milestone

Comments

@jeanmicheldeva
Copy link
Contributor

jeanmicheldeva commented Oct 17, 2024

See here:

let target = il.add(max_width, Register::from(rs1), imm).build();
match (rd.id(), rs1.id(), imm) {
(0, 1, 0) => il.ret(target).append(), // jalr zero, ra, 0
(1, _, _) => il.call(target).append(), // indirect call
(0, _, _) => il.jump(target).append(), // indirect jump
(_, _, _) => {
// indirect jump with storage of next address to non-`ra` register
il.set_reg(
max_width,
Register::from(rd),
il.const_ptr(addr.wrapping_add(inst_len)),
)
.append();
il.jump(target).append();

If rd == rs1, but is neither zero or ra (x0 or x1 resp.), the above code will lift the jalr rd, rs1, imm instruction as follows:

# let's say rd == rs1 == t1
# target = t1 + imm
t1 = pc + 4            # inst_len==4
jump(target) <=> jump(t1 + imm) <=> jump(pc + 4 + imm)

Whereas the intended code should be lifted as:

tmp_register = t1 + imm
t1 = pc + 4
jump(tmp_register)
@jeanmicheldeva
Copy link
Contributor Author

I patched it as follows, and it seems to work:

let target = il.add(max_width, Register::from(rs1), imm).build();
match (rd.id(), rs1.id(), imm) {
    (0, 1, 0) => il.ret(target).append(),  // jalr zero, ra, 0
    (1, _, _) => il.call(target).append(), // indirect call
    (0, _, _) => il.jump(target).append(), // indirect jump
    (_, _, _) => {
        // indirect jump with storage of next address to non-`ra` register
        if rd.id() == rs1.id() {
            let tmp: llil::Register<Register<D>> = llil::Register::Temp(0);
            il.set_reg(max_width, tmp, target).append();
            il.set_reg(
                max_width,
                Register::from(rd),
                il.const_ptr(addr.wrapping_add(inst_len)),
            )
            .append();
            il.jump(tmp).append();
        } else {
            il.set_reg(
                max_width,
                Register::from(rd),
                il.const_ptr(addr.wrapping_add(inst_len)),
            )
            .append();
            il.jump(target).append();
        }
    }
}

However, I encountered another problem: "functions" that are called with jalr rd, rs1, imm usually end with jr rd. Obviously, the control-flow is the target "function" is unresolved, since rd does not have defined value in the context of the function.

As a matter of fact, this target "function" (ending with jr rd) is most probably just one or multiple basic blocks that are shared between multiple calling functions for space optimization.

Is there any way to tell BN to treat these target "functions" as such ? (i.e. not functions, but basic blocks to reattach to different parent functions).
Doing this manually would be the equivalent of "Append function tail" feature in IDA ; that would be great is BN would handle this automatically (BN usually has no problem with shared basic blocks IIRC)

Or maybe my patch is incomplete, and this is already handled.

Anyway, thanks in advance

@jeanmicheldeva
Copy link
Contributor Author

I found that modifying the following code solves the problem:

Op::Jalr(ref i) => {
// TODO handle the calls with rs1 == 0?
if i.rd().id() == 0 {
let branch_type = if i.rs1().id() == 1 {
BranchInfo::FunctionReturn
} else {
BranchInfo::Unresolved
};
res.add_branch(branch_type, None);
}
}

There is a missing branch information for the case where rd != 0, so I added the following else clause

Op::Jalr(ref i) => {
    // TODO handle the calls with rs1 == 0?
    if i.rd().id() == 0 {
        let branch_type = if i.rs1().id() == 1 {
            BranchInfo::FunctionReturn
        } else {
            BranchInfo::Unresolved
        };

        res.add_branch(branch_type, None);
    } else {
        res.add_branch(BranchInfo::Unresolved, None); // This
    }
}

And now everything works as intended

@xusheng6
Copy link
Member

@jeanmicheldeva would you like to submit a PR for this?

@xusheng6 xusheng6 added Type: Bug Issue is a non-crashing bug with repro steps Component: Architecture Issue needs changes to an architecture plugin Impact: Low Issue is a papercut or has a good, supported workaround Effort: Low Issue should take < 1 week Arch: RISC-V Issues with the RISC-V architecture plugin labels Oct 29, 2024
@plafosse plafosse added this to the Gallifrey milestone Oct 29, 2024
@emesare emesare assigned emesare and unassigned galenbwill Dec 4, 2024
@emesare
Copy link
Member

emesare commented Dec 24, 2024

Half of this issue is done with the other portion (adding unresolved branch for rd != 0) remains to be solved.

For the other fix (the one named in the issue) see #6213

@emesare
Copy link
Member

emesare commented Dec 24, 2024

For the sake of discoverability I am going to make a new issue to describe rd != 0 not emitting branch info.

Here: #6273

@emesare emesare closed this as completed Dec 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Arch: RISC-V Issues with the RISC-V architecture plugin Component: Architecture Issue needs changes to an architecture plugin Effort: Low Issue should take < 1 week Impact: Low Issue is a papercut or has a good, supported workaround Type: Bug Issue is a non-crashing bug with repro steps
Projects
None yet
Development

No branches or pull requests

5 participants