Dynamic code generation--how to fill in necessary values and

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
purevoid

Dynamic code generation--how to fill in necessary values and

Post 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:

Code: Select all

void test_isr() {
  my_isr();
}
I'm obviously doing something wrong. Perhaps I can't call my ISR code like that, and need to test something a little simpler?
richie

Re:Dynamic code generation--how to fill in necessary values

Post 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.
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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.
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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?
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Dynamic code generation--how to fill in necessary values

Post 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.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Dynamic code generation--how to fill in necessary values

Post 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 ...
distantvoices
Member
Member
Posts: 1600
Joined: Wed Oct 18, 2006 11:59 am
Location: Vienna/Austria
Contact:

Re:Dynamic code generation--how to fill in necessary values

Post 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.
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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 =)
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Dynamic code generation--how to fill in necessary values

Post 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);
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Dynamic code generation--how to fill in necessary values

Post 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.
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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...
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Dynamic code generation--how to fill in necessary values

Post by Candy »

Yes. Yes.
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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 =(
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Dynamic code generation--how to fill in necessary values

Post 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.
purevoid

Re:Dynamic code generation--how to fill in necessary values

Post 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.
Post Reply