Skip to content

Commit 3154f4b

Browse files
committed
Automatically align %rsp for clean calls.
Aligns %rsp to a 16byte boundary as required by the ABI. Can be disabled using `noalign'.
1 parent 1ec6835 commit 3154f4b

7 files changed

Lines changed: 60 additions & 20 deletions

File tree

doc/e9patch-programming-guide.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ function should be called after the patched instruction
185185
The options are:
186186

187187
* `clean`/`naked` for clean/naked calls.
188+
* `noalign` disables stack alignment for clean calls.
188189
* `before`/`after`/`replace` for inserting the
189190
call before/after the instruction, or replacing
190191
the instruction by the call.
@@ -194,7 +195,11 @@ responsible for preserving the CPU state, and not E9Tool.
194195
This usually means the function must be implemented in assembly.
195196
For some applications, this can enable more optimized code.
196197
The default is `clean`, which means E9Tool will automatically
197-
generate code for saving/restoring the CPU state.
198+
generate code for saving/restoring the CPU state and aligning
199+
the stack pointer to a 16-byte boundary.
200+
Stack alignment is required by the System V ABI, but can be
201+
disabled using `noalign` option.
202+
The `naked` option automatically implies `noalign`.
198203

199204
The `before`/`after`/`replace` option specifies where the
200205
instrumented call should be placed.

e9frontend.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,7 +2472,8 @@ static void sendLeaFromStackToR64(FILE *out, int32_t offset, int regno)
24722472
* Send a call ELF trampoline.
24732473
*/
24742474
unsigned e9frontend::sendCallTrampolineMessage(FILE *out, const char *name,
2475-
const std::vector<Argument> &args, bool clean, CallKind call)
2475+
const std::vector<Argument> &args, bool clean, bool noalign,
2476+
CallKind call)
24762477
{
24772478
sendMessageHeader(out, "trampoline");
24782479
sendParamHeader(out, "name");
@@ -2499,12 +2500,37 @@ unsigned e9frontend::sendCallTrampolineMessage(FILE *out, const char *name,
24992500
for (int i = 0; rsave[i] >= 0; i++, num_rsave++)
25002501
sendPush(out, 0, (call != CALL_AFTER), getReg(rsave[i]));
25012502

2502-
// Load the arguments (if necessary):
2503+
// Load the arguments:
25032504
fputs("\"$loadArgs\",", out);
25042505

2506+
// Align the stack (if necessary):
2507+
if (!noalign)
2508+
{
2509+
assert(clean);
2510+
// Ensure %rsp is 16-byte aligned as per the ABI:
2511+
// Both %rax and %rflags are saved and can be clobbered.
2512+
//
2513+
// mov %rsp,%rax
2514+
// and $-16,%rsp
2515+
// push %rax
2516+
// push %rax
2517+
//
2518+
fprintf(out, "%u,%u,%u,", 0x48, 0x89, 0xe0);
2519+
fprintf(out, "%u,%u,%u,%u,", 0x48, 0x83, 0xe4, 0xf0);
2520+
fprintf(out, "%u,", 0x50);
2521+
fprintf(out, "%u,", 0x50);
2522+
}
2523+
25052524
// Call the function:
25062525
fprintf(out, "%u,\"$function\",", 0xe8); // callq function
25072526

2527+
// Un-align the stack (if necessary):
2528+
if (!noalign)
2529+
{
2530+
// pop %rsp
2531+
fprintf(out, "%u,", 0x5c);
2532+
}
2533+
25082534
// Restore the state:
25092535
fputs("\"$restoreState\",", out);
25102536

e9frontend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ extern unsigned sendPassthruTrampolineMessage(FILE *out);
148148
extern unsigned sendPrintTrampolineMessage(FILE *out);
149149
extern unsigned sendTrapTrampolineMessage(FILE *out);
150150
extern unsigned sendCallTrampolineMessage(FILE *out, const char *name,
151-
const std::vector<Argument> &args, bool clean = true,
151+
const std::vector<Argument> &args, bool clean = true, bool noalign = true,
152152
CallKind call = CALL_BEFORE);
153153
extern unsigned sendTrampolineMessage(FILE *out, const char *name,
154154
const char *template_);

e9parser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum Token
7171
TOKEN_MNEMONIC,
7272
TOKEN_NAKED,
7373
TOKEN_NEXT,
74+
TOKEN_NOALIGN,
7475
TOKEN_OFFSET,
7576
TOKEN_OP,
7677
TOKEN_PASSTHRU,
@@ -167,6 +168,7 @@ static const TokenInfo tokens[] =
167168
{"mnemonic", TOKEN_MNEMONIC},
168169
{"naked", TOKEN_NAKED},
169170
{"next", TOKEN_NEXT},
171+
{"noalign", TOKEN_NOALIGN},
170172
{"offset", TOKEN_OFFSET},
171173
{"op", TOKEN_OP},
172174
{"passthru", TOKEN_PASSTHRU},

e9test.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ export LIMIT=99999999999
2626

2727
for ACTION in \
2828
'passthru' \
29-
'call entry@nop' \
29+
'call[noalign] entry@nop' \
3030
'call[naked,after] entry@nop' \
3131
'call entry(asm,instr,rflags,rdi,rip,addr,target,next)@nop' \
3232
'call entry(&rsp,&rax,&rsi,&rdi,&r8,&r15,staticAddr,0x1234)@nop' \
33-
'call entry(&op[0],&src[0],&dst[0],&op[1],&src[1],&dst[1],&dst[7],&src[7])@nop' \
34-
'call entry(reg[0],&reg[0],imm[0],&imm[0],&mem[0],reg[1],&reg[1],imm[1])@nop' \
33+
'call[noalign] entry(&op[0],&src[0],&dst[0],&op[1],&src[1],&dst[1],&dst[7],&src[7])@nop' \
34+
'call[noalign] entry(reg[0],&reg[0],imm[0],&imm[0],&mem[0],reg[1],&reg[1],imm[1])@nop' \
3535
'plugin[example]' \
3636
'print'
3737
do

e9tool.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,21 @@ struct Action
218218
const char * const filename;
219219
const char * const symbol;
220220
const ELF * elf;
221-
Plugin *plugin;
221+
Plugin * const plugin;
222222
void *context;
223-
std::vector<Argument> args;
224-
bool clean;
225-
CallKind call;
223+
const std::vector<Argument> args;
224+
const bool clean;
225+
const bool noalign;
226+
const CallKind call;
226227

227228
Action(const char *string, MatchEntries &&entries, ActionKind kind,
228229
const char *name, const char *filename, const char *symbol,
229230
Plugin *plugin, const std::vector<Argument> &&args, bool clean,
230-
CallKind call) :
231+
bool noalign, CallKind call) :
231232
string(string), entries(std::move(entries)), kind(kind),
232233
name(name), filename(filename), symbol(symbol), elf(nullptr),
233234
plugin(plugin), context(nullptr), args(args), clean(clean),
234-
call(call)
235+
noalign(noalign), call(call)
235236
{
236237
;
237238
}
@@ -635,7 +636,7 @@ static Action *parseAction(const char *str, MatchEntries &entries)
635636
// Parse call or plugin (if necessary):
636637
CallKind call = CALL_BEFORE;
637638
bool clean = false, naked = false, before = false, after = false,
638-
replace = false, conditional = false;
639+
replace = false, conditional = false, noalign = false;
639640
const char *symbol = nullptr;
640641
const char *filename = nullptr;
641642
Plugin *plugin = nullptr;
@@ -670,6 +671,8 @@ static Action *parseAction(const char *str, MatchEntries &entries)
670671
conditional = true; break;
671672
case TOKEN_NAKED:
672673
naked = true; break;
674+
case TOKEN_NOALIGN:
675+
noalign = true; break;
673676
case TOKEN_REPLACE:
674677
replace = true; break;
675678
default:
@@ -856,6 +859,7 @@ static Action *parseAction(const char *str, MatchEntries &entries)
856859
error("failed to parse call action; only one of the `before', "
857860
"`after', `replace' and `conditional' attributes can be used "
858861
"together");
862+
noalign = (naked? true: noalign);
859863
clean = (clean? true: !naked);
860864
call = (after? CALL_AFTER:
861865
(replace? CALL_REPLACE:
@@ -909,7 +913,7 @@ static Action *parseAction(const char *str, MatchEntries &entries)
909913
}
910914

911915
Action *action = new Action(str, std::move(entries), kind, name, filename,
912-
symbol, plugin, std::move(args), clean, call);
916+
symbol, plugin, std::move(args), clean, noalign, call);
913917
return action;
914918
}
915919

@@ -1436,6 +1440,8 @@ static void usage(FILE *stream, const char *progname)
14361440
fputc('\n', stream);
14371441
fputs("\t\t\t- OPTION is one of:\n", stream);
14381442
fputs("\t\t\t * \"clean\"/\"naked\" for clean/naked calls.\n", stream);
1443+
fputs("\t\t\t * \"noalign\" disables stack alignment for clean calls.\n",
1444+
stream);
14391445
fputs("\t\t\t * \"before\"/\"after\"/\"replace\"/\"conditional\" for\n",
14401446
stream);
14411447
fputs("\t\t\t inserting the call before/after the instruction, or\n",
@@ -1904,7 +1910,8 @@ int main(int argc, char **argv)
19041910
if (j == have_call.end())
19051911
{
19061912
sendCallTrampolineMessage(backend.out, action->name,
1907-
action->args, action->clean, action->call);
1913+
action->args, action->clean, action->noalign,
1914+
action->call);
19081915
have_call.insert(action->name);
19091916
}
19101917
break;

examples/print.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ static char *byte_to_str(unsigned char x, char *str)
4242
/*
4343
* Entry point.
4444
*
45-
* call entry(addr,instr,instr.count,asm)@print
45+
* call entry(addr,instr,size,asm)@print
4646
*/
47-
void entry(void *addr, const unsigned char *instr, unsigned instr_count,
47+
void entry(const void *addr, const unsigned char *instr, unsigned long size,
4848
const char *_asm)
4949
{
5050
char buf[512];
@@ -56,11 +56,11 @@ void entry(void *addr, const unsigned char *instr, unsigned instr_count,
5656
for (i = (unsigned)(str - buf); i < 16; i++)
5757
*str++ = ' ';
5858
str = str_cat("\33[0m: \33[33m", str);
59-
for (i = 0; i < instr_count; i++)
59+
for (i = 0; i < size; i++)
6060
{
6161
str = byte_to_str(instr[i], str);
6262
*str++ = ' ';
63-
if (i == 7 && instr_count > 8)
63+
if (i == 7 && size > 8)
6464
{
6565
str = str_cat("\33[32m", str);
6666
str = str_cat(_asm, str);

0 commit comments

Comments
 (0)