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

NOP instructions for code alignment #336

Open
Paliha opened this issue Jun 20, 2021 · 20 comments
Open

NOP instructions for code alignment #336

Paliha opened this issue Jun 20, 2021 · 20 comments

Comments

@Paliha
Copy link

Paliha commented Jun 20, 2021

As far as I understand, the database has been updated for a long time, although double nops appeared in the engine in the spring of 2020. But the instruction mnemonics are not unique in the list. Is there an option in the engine to use the nop instruction to align the code?
If so, show an example.
90 NOP1_OVERRIDE_NOP
6690 NOP2_OVERRIDE_NOP
0f1f00 NOP3_OVERRIDE_NOP
0f1f4000 NOP4_OVERRIDE_NOP
0f1f440000 NOP5_OVERRIDE_NOP
660f1f440000 NOP6_OVERRIDE_NOP
0f1f8000000000 NOP7_OVERRIDE_NOP
0f1f840000000000 NOP8_OVERRIDE_NOP
660f1f840000000000 NOP9_OVERRIDE_NOP
66660f1f840000000000 NOP10_OVERRIDE_NOP
6666660f1f840000000000 NOP11_OVERRIDE_NOP
666666660f1f840000000000 NOP12_OVERRIDE_NOP
66666666660f1f840000000000 NOP13_OVERRIDE_NOP
6666666666660f1f840000000000 NOP14_OVERRIDE_NOP
666666666666660f1f840000000000 NOP15_OVERRIDE_NOP
https://coderoad.ru/25545470/%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D1%8B%D0%B9-%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%B1%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9-NOPs-%D0%BE%D0%B1%D1%89%D0%B5%D0%BF%D1%80%D0%B8%D0%BD%D1%8F%D1%82%D0%B0%D1%8F-macros-%D0%B8%D0%BB%D0%B8-%D0%B4%D1%80%D1%83%D0%B3%D0%B0%D1%8F-%D0%BD%D0%BE%D1%82%D0%B0%D1%86%D0%B8%D1%8F

@kobalicek
Copy link
Member

kobalicek commented Jun 20, 2021

AsmJit has a known NOP sequence list, which can be activated by:

addEncodingOptions(BaseEmitter::kEncodingOptionOptimizedAlign)

The table looks like this:

{ 0x90 },
{ 0x66, 0x90 },
{ 0x0F, 0x1F, 0x00 },
{ 0x0F, 0x1F, 0x40, 0x00 },
{ 0x0F, 0x1F, 0x44, 0x00, 0x00 },
{ 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 },
{ 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 },
{ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }

It's not active by default and it doesn't favor any CPU vendor.

Does this answer the question?

@Paliha
Copy link
Author

Paliha commented Jun 22, 2021

."....Does this answer the question?"
not completely. with nops instruction bytecode, no need to use asmjit. In mnemonics, the difference is either word size or segment case, but that's not for your entire list. In my assembly of asmjit there is no such api, fresh assemblies are not compiled.

@kobalicek
Copy link
Member

kobalicek commented Jun 22, 2021

You can either use align() to emit nops or you can also emit nops directly if you need a custom one:

Error nop();
Error nop(const Gp& o0);
Error nop(const Mem& o0);
Error nop(const Gp& o0, const Gp& o1);
Error nop(const Mem& o0, const Gp& o1);

If you don't have the API then maybe it's time to update asmjit :)

BTW I don't think I understand the question, I think you should elaborate and be more specific in what you want / expect.

@Paliha
Copy link
Author

Paliha commented Jun 25, 2021

updated, but did not connect aling api yet.
the question is better to show with an example
00000001518F7003 | 66 66 66 66 2E 0F 1F 84 00 00 00 00 00 | NOP WORD PTR CS:[RAX + RAX], AX
00000001518F7010 | 66 66 66 66 66 66 2E 0F 1F 84 00 00 00 00 00 | NOP WORD PTR CS:[RAX + RAX], AX
but jit encodes as
2E 66 0F 1F 04 00 | NOP WORD PTR CS:[RAX + RAX], AX
although the ultimate challenge is to encode a code like this
lea rsp, ss:[rsp-0x28]
push rax
push rcx
push rdx
cmp byte ptr ds:[0x00000001518F7000], 0x0
jne short @L00000002
lea rax, ds:[0x0000000140000000]
lea rcx, ds:[0x00000001518F7064]
NOP WORD PTR CS:[RAX + RAX], AX
@L00000001:
mov rdx, qword ptr ds:[rax]
add rax, 0x1000
cmp rax, rcx
jl short @L00000001
mov byte ptr ds:[0x00000001518F7000], 0x1

@L00000002:
pop rdx
pop rcx
pop rax
lea rsp, ss:[rsp+0x28]
jmp 0x00000001518F6000

@kobalicek
Copy link
Member

Could you share some code instead and describe what you want to do? I'm still unable to get what you want / what are you missing. AsmJit would not generate multiple 66 66 prefix bytes with any instruction at the moment, only align() can do that. But you can still emit that yourself / emit NOP explicitly:

a.db(0x66);
a.db(0x66);
a.db(0x66);
a.db(0x66);
a.nop();

for example, if that's what you need.

@UnlimitedChild
Copy link

The goal is to create a block of code with a specific size with a specific predefined alignment. All this needs to be done without unnecessary action.

@kobalicek
Copy link
Member

I still don't understand what's the problem with align()?

@kobalicek
Copy link
Member

Can you elaborate about "unnecessary action"? I really don't get it sorry :)

@Paliha
Copy link
Author

Paliha commented Jun 26, 2021

@kobalicek
i am not using BaseEmitter, and its statement syntax.
I have a classic intel syntax asm instructions.
CodeHolder code;
Error err = code.init(coEnv, select.start);
x86::Assembler a(&code);
AsmParser p(&a);
err = p.parse(instruction);
to my surprise, I found that the engine in the x86 :: Assembler mode encodes more than one instruction, and understands the jumps by labels (+ short_long), and determines_encodes itself. distances of transitions.
But Intel's double nop syntax is limited to 1,3,4,5,6 bytes, the rest come with the 0x66 index.
in this mode, if there is an index in the line, the engine generates an error.
I can calculate the alignment with the help myself, but I don't understand how to code it in blocks.
Additionally, I have one more question.
Everything is clear with the error index, but if the encoding is more than one instruction, how to determine which instruction in the block is not valid?

@kobalicek
Copy link
Member

Ok, and can you show the code you are assembling, binary that asmjit encodes, and binary that is encoded by some other assembler? Because it's really hard to tell without some sample whether asmjit does something right or wrong.

@Paliha
Copy link
Author

Paliha commented Jun 26, 2021

@kobalicek
in fact from the available open source I know of only 3 ASM engines.
Xed has no analogue of ASMTK, Keystone is rarely updated, basically I have been using yours for a long time.
From errors known to me:
Asmjit Error: InvalidInstruction (0000001b) | addr: 00000001518F7064 instr->prefetcht0 byte ptr ds:[r15]
Asmjit Error: Invalid instruction (00000016) | addr: 0000000130000067 instr->prefetcht0 byte ptr ds:[r15+0x1C0]

@kobalicek
Copy link
Member

If you remove byte ptr from the prefetcht0 it would work - asmjit is maybe too strict in this case, but that can be fixed.

@kobalicek
Copy link
Member

I fixed the prefech strictness, but not sure what else to fix.

@Paliha
Copy link
Author

Paliha commented Jul 4, 2021

thanks for the fixes, the code has been updated
returning to the topic. In my case, you can use the ".align" directive, but it's not entirely clear with the argument for alignment.

uint32_t i = uint32_t(Support::alignUpDiff<size_t>(offset(), alignment));

@kobalicek
Copy link
Member

It aligns the code buffer to the specified alignment, in bytes, what is not clear about that?

@Paliha
Copy link
Author

Paliha commented Jul 5, 2021

the logic of the derivative is clear, the conditions for the code are not clear.
if (tmp.u64> std :: numeric_limits <uint32_t> :: max () ||! Support :: isPowerOf2 (tmp.u64))
all odd values (3; 5; 7; 9) fail with kErrorInvalidState.
uint32_t i = uint32_t (Support :: alignUpDiff <size_t> (offset (), alignment));
for this function, the base address of the nops instruction is absent, the calculations are from offset ().
offset = the size of the code already encoded.
the result is not affected by the "alignment" value.

lea rsp, ss:[rsp-0x28]
push rax
push rcx
push rdx
cmp byte ptr ds:[0x00000001518F7000], 0x0
jne short @L_00000001518F7057
lea rax, ds:[0x0000000140000000]
lea rcx, ds:[0x00000001518F7064]
  .align 9

@L_00000001518F7042:
mov rdx, qword ptr ds:[rax]
add rax, 0x1000
cmp rax, rcx
jl short @L_00000001518F7042
mov byte ptr ds:[0x00000001518F7000], 0x1

@L_00000001518F7057:
pop rdx
pop rcx
pop rax
lea rsp, ss:[rsp+0x28]
jmp 0x00000001518F6000

@kobalicek
Copy link
Member

I think the most important information was the base address of the nops instruction is absent - yeah this can be fixed.

@Paliha
Copy link
Author

Paliha commented Jul 5, 2021

I have already corrected. the alignment is encoded in automation.
the argument for alignment is still declarative, it does not matter.
I had to comment out 2 parity checks:

if (tmp.u64> std :: numeric_limits <uint32_t> :: max () / * ||! Support :: isPowerOf2 (tmp.u64) * /)
and
if (ASMJIT_UNLIKELY (/ *! Support :: isPowerOf2 (alignment) || * / alignment> Globals :: kMaxAlignment))
return reportError (DebugUtils :: errored (kErrorInvalidArgument));
+
// uint32_t i = uint32_t (Support :: alignUpDiff <size_t> (ssize, alignment));
uint32_t i = 0x10 - (offset () + _code -> _ baseAddress)% 0x10;
Hope you make it more beautiful.
In case of a jump length error InvalidDisplacement (short or long), how do I get the line number?
there is the size of the already encoded buffer, but in the process there is a nop alignment.

@kobalicek
Copy link
Member

I can fix the issue with base address, but the power of 2 check is needed, that cannot just go away

@Paliha
Copy link
Author

Paliha commented Jul 5, 2021

You are the author, you decide what to leave and what to fix.
So all the same, how to get the line number and mnemonic of the instruction that caused the InvalidDisplacement error if more than one instruction is encoded in the input?
The log and analogs only output what is already encoded.
The error is not always at the end of the log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants