Page 1 of 2
Dynamic code generation--how to fill in necessary values and
Posted: Sat Dec 18, 2004 6:19 pm
by purevoid
I have code for an ISR, but I'm trying to call it as a normal function to try test that I've got things right...
Code: Select all
char isr_wrapper[] = {
/* pusha, push gs, fs, es, ds */
0x60, 0x0F, 0xA8, 0x0F, 0xA0, 0x06, 0x1E,
/* push parameter 2 onto stack [8]-[11] */
0x68, 0x00, 0x00, 0x00, 0x00,
/* push parameter 1 onto stack [13]-[16] */
0x68, 0x00, 0x00, 0x00, 0x00,
/* call function [18]-[21] */
0x9A, 0x00, 0x00, 0x00, 0x00,
/* pop ds, es, fs, gs, popa, iret */
0x1F, 0x07, 0x0F, 0xA1, 0x0F, 0x09, 0x61, 0xCF
};
char *test_isr = NULL;
typedef void (*isr_caller)();
isr_caller my_isr;
At present, since I was getting a seg-fault in linux, with no output, I'm trying to call printf just to see if things work, but I'm having the same problem. I'm not sure if it's a problem with my code, or a problem with how I'm filling in the necessary parts of the dynamic code, which is done as below:
Code: Select all
char *str = "%s\n";
char *str_param = "An interrupt from dynamically generated code";
void init_isr() {
test_isr = (char *)malloc(30);
memcpy(test_isr, isr_wrapper, 30);
unsigned long param2 = (unsigned long)str_param;
unsigned long param1 = (unsigned long)str;
unsigned long address = (unsigned long)&printf;
test_isr[ 8] = (param2 >> 0) & 0xFF;
test_isr[ 9] = (param2 >> 8) & 0xFF;
test_isr[10] = (param2 >> 16) & 0xFF;
test_isr[11] = (param2 >> 24) & 0xFF;
test_isr[13] = (param1 >> 0) & 0xFF;
test_isr[14] = (param1 >> 8) & 0xFF;
test_isr[15] = (param1 >> 16) & 0xFF;
test_isr[16] = (param1 >> 24) & 0xFF;
test_isr[18] = (address >> 0) & 0xFF;
test_isr[19] = (address >> 8) & 0xFF;
test_isr[20] = (address >> 16) & 0xFF;
test_isr[21] = (address >> 24) & 0xFF;
my_isr = (isr_caller)test_isr;
}
And then I call the code with:
I'm obviously doing something wrong. Perhaps I can't call my ISR code like that, and need to test something a little simpler?
Re:Dynamic code generation--how to fill in necessary values
Posted: Sat Dec 18, 2004 6:35 pm
by richie
You pushed two parameters on the stack but never remove them from there. Before the function-call you pushed parameter 1(and 2). After the function is called you pop this value in ds (and es).
Increase the stackpointer by 8 bytes to remove the two parameters from the stack before pushing ds.
Re:Dynamic code generation--how to fill in necessary values
Posted: Sat Dec 18, 2004 6:46 pm
by purevoid
Okay, I tried something simpler. No params, calling a function I declared, and similar to above, except only setting up address of the function. I then call in the same way (my_isr()), and still get the seg-fault.
Re:Dynamic code generation--how to fill in necessary values
Posted: Sat Dec 18, 2004 7:06 pm
by purevoid
And tried some debugging printf calls:
Debugging calls:
Code: Select all
my_isr = (isr_caller)test_isr;
printf("0x%08x\n", param2);
printf("0x%08x\n", param1);
printf("0x%08x\n\n", address);
for ( i = 0; i < 30; ++i ) {
printf("0x%02x ", test_isr[i]);
}
Output:
Code: Select all
0x00000001
0x0805f564
0x0805aee4
0x60 0x0f 0xffffffa8 0x0f 0xffffffa0 0x06 0x1e 0x68 0x01 0x00 0x00 0x00 0x68 0x64 0xfffffff5 0x05 0x08 0xffffff9a 0xffffffe4 0xffffffae 0x05 0x08 0xffffff83 0xffffffc4 0x08 0x1f 0x07 0x0f 0xffffffa1 0x0f
From what I'm seeing here, gcc is producing the incorrect code from the beginning. What to do about it?
Re:Dynamic code generation--how to fill in necessary values
Posted: Sun Dec 19, 2004 4:29 am
by Candy
purevoid wrote:
And tried some debugging printf calls:
Debugging calls:
Code: Select all
my_isr = (isr_caller)test_isr;
printf("0x%08x\n", param2);
printf("0x%08x\n", param1);
printf("0x%08x\n\n", address);
for ( i = 0; i < 30; ++i ) {
printf("0x%02x ", test_isr[i]);
}
Output:
Code: Select all
0x00000001
0x0805f564
0x0805aee4
0x60 0x0f 0xffffffa8 0x0f 0xffffffa0 0x06 0x1e 0x68 0x01 0x00 0x00 0x00 0x68 0x64 0xfffffff5 0x05 0x08 0xffffff9a 0xffffffe4 0xffffffae 0x05 0x08 0xffffff83 0xffffffc4 0x08 0x1f 0x07 0x0f 0xffffffa1 0x0f
From what I'm seeing here, gcc is producing the incorrect code from the beginning. What to do about it?
There's this one thing, you don't have unsigned chars extended. Your chars are sign-extended, ignore the first 6 F's, or add an -funsigned-char or sth. to your compile line.
Two, you're still using the wrong call code and not popping enough off the stack. See also your other thread on the same subject.
Three, this is a second thread about one subject. Don't make another thread about something you are already discussing about.
Fourth, imo this would fit better in general programming.
Re:Dynamic code generation--how to fill in necessary values
Posted: Mon Dec 20, 2004 5:02 am
by Pype.Clicker
Candy wrote:
Fourth, imo this would fit better in general programming.
I don't think so ...
Btw, if i understood correctly, you're running that as a normal Linux program, right ? So you can simply use GDB or DDD to find out why you get a segfault ...
Hint: STDOUT is buffered, so you'll simply see nothing out if the segfault occurs "too early"
Hint++: i don't think Linux kernel likes *at all* you to issue "IRET" instructions in usermode ...
Re:Dynamic code generation--how to fill in necessary values
Posted: Mon Dec 20, 2004 6:40 am
by distantvoices
Hm. I still disagree with the approach of having isr stubs inside *generated code*, but here we are:
What do you think about building a basic layer, which receives an irq.(with gdt, idt and all that stuff you know what i mean) It contains amongst others some stubs surrounding a call. Might look like this:
Code: Select all
hwint1:
EXTERN hwint1_isr
call hwint1_isr ;hwint1 isr is a pointer to a location you have to allocate and initialize with your isr stub.
in your IDT you enter the address of hwint1.
Re:Dynamic code generation--how to fill in necessary values
Posted: Sun Dec 26, 2004 4:29 pm
by purevoid
Ok, I took a break, and then came back to this problem. I discovered I was leaving out the segment selector for push, which has now gotten me a step futher.
Now I'm getting a mysterious error in bochs: "jump protected: cs==0". I assume that means my code segment is set to the null descriptor. Is that correct?
And how would I fix that?
This is my new code:
Code: Select all
unsigned char isr_stub[] = {
//-- pusha, push gs, fs, es, ds
0x60, 0x0F, 0xA8, 0x0F, 0xA0, 0x06, 0x1E,
//-- push ds:32
0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
//-- push ds:32
0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
//-- callback(*closure, Val_unit)
//-- call 32
0xFF, 0x00, 0x00, 0x00, 0x00,
//-- cli, hlt :)
0xFA, 0xF4,
//-- pop ds, es, fs, gs, popa
0x1F, 0x07, 0x0F, 0xA1, 0x0F, 0xA9, 0x61,
//-- iret
0xCF
};
I realise my stack will be messed up (haven't looked what registers to alter yet), but something should happen.
I fill in as below:
Code: Select all
memcpy(isrs[ix].routine, isr_stub, 34);
unsigned long param1 = (unsigned long)Val_unit;
unsigned long param2 = (unsigned long)handler;
unsigned long func = (unsigned long)&callback;
isrs[ix].routine[8] = 0x10;
isrs[ix].routine[9] = param2 & 0xFF;
isrs[ix].routine[10] = (param2 >> 8) & 0xFF;
isrs[ix].routine[11] = (param2 >> 16) & 0xFF;
isrs[ix].routine[12] = (param2 >> 24) & 0xFF;
isrs[ix].routine[14] = 0x10;
isrs[ix].routine[15] = param1 & 0xFF;
isrs[ix].routine[16] = (param1 >> 8) & 0xFF;
isrs[ix].routine[17] = (param1 >> 16) & 0xFF;
isrs[ix].routine[18] = (param1 >> 24) & 0xFF;
isrs[ix].routine[20] = func & 0xFF;
isrs[ix].routine[21] = (func >> 8) & 0xFF;
isrs[ix].routine[22] = (func >> 16) & 0xFF;
isrs[ix].routine[23] = (func >> 24) & 0xFF;
I've tried setting the segment selector to both 0x08 (for CS), and 0x10 (for DS), but still have the same problem.
If someone knows what I'm doing wrong here, it'd be greatly appreciated =)
Re:Dynamic code generation--how to fill in necessary values
Posted: Tue Dec 28, 2004 8:24 am
by Pype.Clicker
I think you should focus on having a working ISR before you attempt to make it a pattern for code generator ...
If your comments are correct, you're missing "add esp,8" (or somethin') to keep the stack clean before you return for the handler (e.g. you'll pop as a return segment something else)
what about something more readable like
Code: Select all
ISR_template:
pushad
push ds, es, fs, gs
ISR_arg_1:
push 0x12345678
ISR_arg_2:
push 0x12345678
ISR_target:
call 0x12345678
add esp,8
pop gs,fs,es,ds
popad
iret
ISR_END:
and using it with
Code: Select all
*(long*)(ISR_arg_1++)=real_arg1;
*(long*)(ISR_arg_2++)=real_arg2;
*(long*)(ISR_target++)=real_target;
memcpy (isr,isr_template,ISR_END-ISR_TEMPLATE);
Re:Dynamic code generation--how to fill in necessary values
Posted: Tue Dec 28, 2004 1:43 pm
by Candy
Pype.Clicker wrote:
what about something more readable like
Code: Select all
ISR_template:
pushad
push ds, es, fs, gs
ISR_arg_1:
push 0x12345678
ISR_arg_2:
push 0x12345678
ISR_target:
call 0x12345678
add esp,8
pop gs,fs,es,ds
popad
iret
ISR_END:
and using it with
Code: Select all
*(long*)(ISR_arg_1++)=real_arg1;
*(long*)(ISR_arg_2++)=real_arg2;
*(long*)(ISR_target++)=real_target;
memcpy (isr,isr_template,ISR_END-ISR_TEMPLATE);
Almost.... the ++'s only work after the entire line has been processed. I would try to use +1, and make sure they are char *'s before you add to them. However, the idea about just coding it in asm and then linking it in using ld is quite a nice one. You should try that.
Re:Dynamic code generation--how to fill in necessary values
Posted: Tue Dec 28, 2004 4:39 pm
by purevoid
So if I understand Pype.Clicker correctly, I just put the assembler code into a file and add to my Makefile, and then I can generate the ISR stubs from that assembly code as shown in my C code?
Seems a much niftier approach =) But the basic approach is the same as if it were a char array of opcodes, etc, right? Just so I've got a handle on how the whole process works...
Re:Dynamic code generation--how to fill in necessary values
Posted: Wed Dec 29, 2004 2:07 am
by Candy
Yes. Yes.
Re:Dynamic code generation--how to fill in necessary values
Posted: Wed Dec 29, 2004 2:49 am
by purevoid
Then I must be doing something wrong still...
I'm using GNU assembler, so I added .global for all the labels, which I assume is the right thing to do.
Then, in my C code, I made extern void * references to the same labels; now I'm getting weird problems.
The output in Bochs is:
Code: Select all
Start: f061e60, End: 559090cf
Length: 1183478383
And that is certainly not right. I also output the ISR routine with printf, and I just get a huge bunch of FFs =(
Re:Dynamic code generation--how to fill in necessary values
Posted: Wed Dec 29, 2004 4:21 am
by Candy
purevoid wrote:
Then I must be doing something wrong still...
I'm using GNU assembler, so I added .global for all the labels, which I assume is the right thing to do.
Then, in my C code, I made extern void * references to the same labels; now I'm getting weird problems.
The output in Bochs is:
Code: Select all
Start: f061e60, End: 559090cf
Length: 1183478383
And that is certainly not right. I also output the ISR routine with printf, and I just get a huge bunch of FFs =(
Try placing & 's in front of the names of the "pointers", I think they might be the content of the address you want.
Re:Dynamic code generation--how to fill in necessary values
Posted: Wed Dec 29, 2004 9:56 am
by purevoid
Getting close =) Now I get an exception: Bounds Check Error (so at least my exception handlers work)... And the length of the asm is reported as 9! I know that's wrong.
The first few bytes are correct (pusha, pushing segments), then I think it tries a near call. The two pushes for parameters are missing, the call itself looks incorrect, no add, and certainly no popping or iret.
Code: Select all
Parameter 1: 0x00000001
Parameter 2: 0x0012128C
Function Address: 0x00114D4C
Start: 1027f0, End: 102816
Length: 9
ISR machine code:
0x60 0x1E 0x06 0x0F 0xA0 0x0F 0xA8 0xFF 0x35 0x00 0x00 0x00...
That's my debugging output now. Just more 0x00 follow. I haven't made any changes to the asm instructions I was shown, so perhaps gcc is 'optimising' it? I have no idea.