Module: OneGadget::ABI
- Defined in:
- lib/one_gadget/abi.rb
Overview
Defines the ABI of different architectures.
Constant Summary collapse
- X86_32 =
Registers of i386.
%w[eax ebx ecx edx edi esi ebp esp] + 0.upto(7).map { |i| "xmm#{i}" }
- X86_64 =
Registers of x86_64.
X86_32 + %w[rax rbx rcx rdx rdi rsi rbp rsp] + 8.upto(15).map { |i| "r#{i}" } + 8.upto(15).map { |i| "xmm#{i}" }
- AARCH64 =
Registers of AArch64.
%w[xzr wzr sp] + 0.upto(30).map { |i| ["x#{i}", "w#{i}"] }.flatten
- RISCV64 =
Registers of RISC-V (RV64), by the ABI names objdump prints: it emits neither the numbered +x0+-+x15+ names nor
fpfors0, so neither is listed. %w[zero ra sp gp tp] + 0.upto(6).map { |i| "t#{i}" } + 0.upto(11).map { |i| "s#{i}" } + 0.upto(7).map { |i| "a#{i}" }
- MIPS =
Registers of MIPS (32-bit), by the names objdump prints: it emits the ABI names rather than the numbered +$0+-+$31+, and
fprather thans8. %w[zero at v0 v1 gp sp fp ra] + 0.upto(3).map { |i| "a#{i}" } + 0.upto(9).map { |i| "t#{i}" } + 0.upto(7).map { |i| "s#{i}" } + 0.upto(1).map { |i| "k#{i}" }
- ARM =
Registers of ARM (32-bit). objdump never prints the numbered name of a register that has a role name: +r10+-+r15+ always appear as +sl+/+fp+/+ip+/+sp+/+lr+/+pc+, so those are what this list holds.
%w[sl fp ip sp lr pc] + 0.upto(9).map { |i| "r#{i}" }
- NARROW_VIEWS =
Names that address part of a wider register rather than storage of their own, mapped to the register they name part of. A write through either name is visible through the other, which is what Emulators::RegisterFile keeps them consistent about.
{ amd64: (%w[ax bx cx dx di si bp sp].map { |reg| ["e#{reg}", "r#{reg}"] } + 8.upto(15).map { |i| ["r#{i}d", "r#{i}"] }).to_h, i386: {}, aarch64: 0.upto(30).to_h { |i| ["w#{i}", "x#{i}"] }.merge('wzr' => 'xzr'), arm: {}, # RISC-V names no part of a register: its 32-bit operations are instructions # of their own (+addiw+, +sext.w+), each writing the whole register. riscv64: {}, # MIPS names no part of a register either. mips: {} }.freeze
- CALLER_SAVED =
Registers a call may destroy, per each ABI's calling convention: the return register, the argument registers and the scratch ones. A callee is free to leave anything here in an arbitrary state, so what it holds afterwards is not something the caller of a gadget can choose. Named by their full registers; the narrower views of each go with it (see NARROW_VIEWS).
{ # SysV amd64: return rax, arguments rdi/rsi/rdx/rcx/r8/r9, scratch r10/r11. amd64: %w[rax rcx rdx rsi rdi r8 r9 r10 r11] + 0.upto(15).map { |i| "xmm#{i}" }, # cdecl i386: return eax, scratch ecx/edx; arguments go on the stack. i386: %w[eax ecx edx] + 0.upto(7).map { |i| "xmm#{i}" }, # AAPCS64: x0-x7 arguments and return, x9-x15 temporaries, x16/x17 the # intra-procedure-call scratch, x18 the platform register. aarch64: (0.upto(7).to_a + 9.upto(18).to_a).map { |i| "x#{i}" }, # AAPCS: r0-r3 arguments and return, ip (r12) the intra-procedure scratch, # and lr, which the call itself overwrites with the return address. arm: %w[r0 r1 r2 r3 ip lr], # RISC-V LP64: a0-a7 arguments (a0/a1 also the return), t0-t6 temporaries, # and ra, which the call itself overwrites with the return address. riscv64: %w[ra] + 0.upto(7).map { |i| "a#{i}" } + 0.upto(6).map { |i| "t#{i}" }, # o32: v0/v1 the return, a0-a3 arguments, t0-t9 temporaries, at the # assembler's scratch, and ra, which the call itself overwrites. gp goes # with them: PIC code reloads it from the stack after every call, because # the callee establishes its own. mips: %w[at v0 v1 gp ra] + 0.upto(3).map { |i| "a#{i}" } + 0.upto(9).map { |i| "t#{i}" } }.freeze
- RETURN_REGISTER =
The register each ABI leaves an integer return value in.
{ amd64: 'rax', i386: 'eax', aarch64: 'x0', arm: 'r0', riscv64: 'a0' }.freeze
Class Method Summary collapse
-
.aarch64 ⇒ Array<String>
Registers' name of aarch64.
-
.all ⇒ Array<String>
Returns all names of registers.
-
.amd64 ⇒ Array<String>
Registers' name of amd64.
-
.arm ⇒ Array<String>
Registers' name of arm (32-bit).
-
.i386 ⇒ Array<String>
Registers' name of i386.
-
.mips ⇒ Array<String>
Registers' name of MIPS (32-bit).
-
.riscv64 ⇒ Array<String>
Registers' name of RISC-V (RV64).
-
.stack_register?(reg) ⇒ Boolean
Checks if the register is a stack-related pointer.
Class Method Details
.aarch64 ⇒ Array<String>
Registers' name of aarch64.
98 99 100 |
# File 'lib/one_gadget/abi.rb', line 98 def aarch64 AARCH64 end |
.all ⇒ Array<String>
Returns all names of registers.
122 123 124 |
# File 'lib/one_gadget/abi.rb', line 122 def all amd64 + aarch64 + arm + riscv64 + mips end |
.amd64 ⇒ Array<String>
Registers' name of amd64.
86 87 88 |
# File 'lib/one_gadget/abi.rb', line 86 def amd64 X86_64 end |
.arm ⇒ Array<String>
Registers' name of arm (32-bit).
104 105 106 |
# File 'lib/one_gadget/abi.rb', line 104 def arm ARM end |
.i386 ⇒ Array<String>
Registers' name of i386.
92 93 94 |
# File 'lib/one_gadget/abi.rb', line 92 def i386 X86_32 end |
.mips ⇒ Array<String>
Registers' name of MIPS (32-bit).
116 117 118 |
# File 'lib/one_gadget/abi.rb', line 116 def mips MIPS end |
.riscv64 ⇒ Array<String>
Registers' name of RISC-V (RV64).
110 111 112 |
# File 'lib/one_gadget/abi.rb', line 110 def riscv64 RISCV64 end |
.stack_register?(reg) ⇒ Boolean
Checks if the register is a stack-related pointer.
130 131 132 |
# File 'lib/one_gadget/abi.rb', line 130 def stack_register?(reg) %w[esp ebp rsp rbp sp x29 s0].include?(reg) end |