Relocatable Code

How do I make my code relocatable?

TIA

“wendy” <wwoods@nospam.arcomcontrols.com> wrote in message
news:903f2c$s5$1@inn.qnx.com
| How do I make my code relocatable?
|
| TIA

Now that’s a loaded question if I’ve ever heard one… :slight_smile:

Uh, what code? If you’re compiling a C program to run under RtP then it’s
already relocatable. If you’re doing an IPL or something else, then it’s a
whole different ballgame alltogether. Shared objects have their own set of
restrictions, but they’re relocatable as well. We need some more information
on what exactly you’re doing before we can be of any real help.

-Warren

wendy <wwoods@nospam.arcomcontrols.com> wrote:

How do I make my code relocatable?

Assuming that you mean a shared object, something like the following

qcc -Vgcc_ntox86 -shared -c shared.c
qcc -Vgcc_ntox86 -shared -o libshared.so.1 -Wl,-soname=libshared.so.1 shared.o

Note that if you have any objects linked into the shared object that
weren’t compiled with the -shared flag, then your app will SIGSEGV when
loading the shared object.

Colin


cburgess@qnx.com

<cburgess@qnx.com> wrote in message news:903nc0$57e$1@nntp.qnx.com
| wendy <wwoods@nospam.arcomcontrols.com> wrote:
| > How do I make my code relocatable?
|
| Assuming that you mean a shared object, something like the following
|
| qcc -Vgcc_ntox86 -shared -c shared.c
| qcc -Vgcc_ntox86 -shared -o libshared.so.1 -Wl,-soname=libshared.so.1
shared.o
|
| Note that if you have any objects linked into the shared object that
| weren’t compiled with the -shared flag, then your app will SIGSEGV when
| loading the shared object.
|
| Colin

Okay so SOME of us don’t need any more info… :slight_smile:

“Warren Peece” <warren@nospam.com> wrote in message
news:903gn0$1sn$1@inn.qnx.com

“wendy” <> wwoods@nospam.arcomcontrols.com> > wrote in message
news:903f2c$s5$> 1@inn.qnx.com> …
| How do I make my code relocatable?
|
| TIA

Now that’s a loaded question if I’ve ever heard one… > :slight_smile:

Uh, what code? If you’re compiling a C program to run under RtP then it’s
already relocatable. If you’re doing an IPL or something else, then it’s
a
whole different ballgame alltogether. Shared objects have their own set
of
restrictions, but they’re relocatable as well. We need some more
information
on what exactly you’re doing before we can be of any real help.

-Warren

Sorry for being vague, I figured you’d need more info… I was hoping for
some prompting.
It’s for an IPL.

  • Wendy

Hmm, my info inferral was incorrect. :v.

Warren Peece <warren@nospam.com> wrote:

cburgess@qnx.com> > wrote in message news:903nc0$57e$> 1@nntp.qnx.com> …
| wendy <> wwoods@nospam.arcomcontrols.com> > wrote:
| > How do I make my code relocatable?
|
| Assuming that you mean a shared object, something like the following
|
| qcc -Vgcc_ntox86 -shared -c shared.c
| qcc -Vgcc_ntox86 -shared -o libshared.so.1 -Wl,-soname=libshared.so.1
shared.o
|
| Note that if you have any objects linked into the shared object that
| weren’t compiled with the -shared flag, then your app will SIGSEGV when
| loading the shared object.
|
| Colin

Okay so SOME of us don’t need any more info… > :slight_smile:


cburgess@qnx.com

“wendy” <wwoods@nospam.arcomcontrols.com> wrote in message
news:903ok4$6s0$1@inn.qnx.com

| Sorry for being vague, I figured you’d need more info… I was hoping for
| some prompting.
| It’s for an IPL.
|
| - Wendy

Let’s start with the basics. CPU type (x86, PPC, MIPS)? Is this a “packaged
board” like the explr2, or AMD eval board or a design of your own? How far
have you gotten, or what have you based your design thus far on (presumably one
of the existing samples)? What in particular do you need to be relocatable,
and why? There will be more questions after you fill in a few of these blanks.

I just finished (if there really is such a concept in firmware :slight_smile: an IPL for an
AMD SC400 (486) custom board. Thus I can be of assistance with another x86 IPL
as they’re going to be mostly the same, but I don’t know much about MIPS or
PPC. I’m sure there’s someone around who knows, however…

-Warren

Let’s start with the basics. CPU type (x86, PPC, MIPS)? Is this a
“packaged
board” like the explr2, or AMD eval board or a design of your own? How
far
have you gotten, or what have you based your design thus far on
(presumably one
of the existing samples)? What in particular do you need to be
relocatable,
and why? There will be more questions after you fill in a few of these
blanks.

It’s an x86, MediaGX. The bios that is used can only search for a video bios
extension,
so we put in a jump to the flash to find the boot loader. Since the flash is
paged, in order
to copy the boot image, we have to move the entire IPL out of the page and
into RAM.
The flash window is 16k.

What I am using is similar to the code under the sc400_ext directory. The
window is at
0xDC000, the ipl is only about 6k so moving 8k should be more than enough. I
want to
move it to 0x7000. In the cstart_int19.s file I added this right before L1:
as follows:

mv_ipl:
movw 0x0DC00, %ax
movw %ax,%ds
xorw %si,%si

movw 0x00700,%ax
movw %ax,%es
movw %di,%di

movw %0x02000,%cx
rep; movsw

pushl %ebp
movl %esp,%ebp
subl $4,%esp
movl $0x07000, -2(%ebp)
movl $L1, -4(%ebp)
call -4(%ebp)

The entire flash is 16M, so to be sure everything’s where it’s suppose to be
I
do the following:
mkipl -ffull -s8k -r ipl-media > ipl-media.pad
cat ipl-media.pad .boot > media.boot
mkipl -ffull -s640k -r media.boot > media_nto.boot

I added min_size = 15360k max_size = 15360k to efs.build. Then I do:
cat media_nto.boot efs.image > image

So image is 16M and everything should be written exactly where I expect
it to be. The ipl should be located on the first page between 0xDC000
and 0xDE000.

Okay, so maybe I don’t need too much prompting :wink:

  • Wendy

A general assembler note (Colin can correct me if I’m wrong :slight_smile:, but movw
0xDC00, %ax I believe is the same thing as movw (0xDC00), %ax. In other words,
without the $ telling the assembler that you want the literal value 0xDC00 to
be moved into AX, it thinks it’s a memory (pointer) reference so you’re
actually getting the word value at 0xDC00 instead of 0xDC00. So what you’ve
shown here is probably not doing what you think it should.

“wendy” <wwoods@nospam.arcomcontrols.com> wrote in message
news:90994i$jon$1@inn.qnx.com
|
| It’s an x86, MediaGX. The bios that is used can only search for a video bios
| extension,
| so we put in a jump to the flash to find the boot loader. Since the flash is
| paged, in order
| to copy the boot image, we have to move the entire IPL out of the page and
| into RAM.
| The flash window is 16k.
|
| What I am using is similar to the code under the sc400_ext directory. The
| window is at
| 0xDC000, the ipl is only about 6k so moving 8k should be more than enough. I
| want to
| move it to 0x7000. In the cstart_int19.s file I added this right before L1:
| as follows:
|
| mv_ipl:
| movw 0x0DC00, %ax
****** ^------ Should add a $ ("$0xDC00")
| movw %ax,%ds
| xorw %si,%si
|
| movw 0x00700,%ax
****** ^------ Should add a $ ("$0x700")
| movw %ax,%es
| movw %di,%di
****** ^------------ Should be xorw
| movw %0x02000,%cx
| rep; movsw
|
| pushl %ebp
| movl %esp,%ebp
| subl $4,%esp
| movl $0x07000, -2(%ebp)
| movl $L1, -4(%ebp)
| call -4(%ebp)
|
| The entire flash is 16M, so to be sure everything’s where it’s suppose to be
| I
| do the following:
| mkipl -ffull -s8k -r ipl-media > ipl-media.pad
| cat ipl-media.pad .boot > media.boot
| mkipl -ffull -s640k -r media.boot > media_nto.boot
|
| I added min_size = 15360k max_size = 15360k to efs.build. Then I do:
| cat media_nto.boot efs.image > image
|
| So image is 16M and everything should be written exactly where I expect
| it to be. The ipl should be located on the first page between 0xDC000
| and 0xDE000.
|
| Okay, so maybe I don’t need too much prompting :wink:
|
| - Wendy

Since you’ve added a jump to your code from the BIOS, we don’t have to worry
about finding the extension signature and so on. Did you remove the 6 zero
bytes at _start in cstart_int19.s? I’m not sure what exactly they’re doing
there in the first place, but if you do a long jump to $0xDC00:0000 bad
things™ will happen if you don’t take them out. Do you indeed perform a far
(long) jump to that address? If not, you should. That will set CS to 0xDC00
(just keeping track of where we are).

So “mv_ipl” is going to be at DC00:0000. If you take a look at x86bios.lnk,
you’ll see that the linker is going to assume our code segment is at 0x0000 and
is 4k (0x1000) in size. The size isn’t really important, unless you fill up
the code segment then ld will complain and you’ll need to bump that value up.
Note that that particular value is for code only, not data. Even though your
segment values will never actually be 0x0000, leaving the address 0 in the
linker file will ensure that all of your offsets begin at 0, relative to the
code segment which is what we’re after.

The code at L1 in cstart_int19.s, although undocumented, appears to be setting
the interrupt vector segment and offset values for int 0x19 to L2. This is
supposedly where control is transferred once the POST routines are finished.
I’m not sure if this really applies to your case, since you inserted a direct
jump in the BIOS itsself, your code is considered to be part of the POST
routines. This code, according to the linker, is based at segment 0x0000, and
that’s the address that’s being stored in the interrupt vector table (although
to actual offset for L2 is correct). When you’re executing in your FLASH
window, the segment address is wrong, and when you’re executing in RAM after
copying to address 0x7000 it’s also wrong. You’ll want to use segment address
$0x0700 instead of $0x0000 so all of your offsets will remain happy, if you
should ever happen to invoke int 19h for some strange reason. Perhaps that
whole part can be left out, I’m not sure. At any rate, with the mods as shown
above, your IPL should be fully relocated to 0700:0000, but I think your
control transfer to L1 needs a little work.

What you want to do, is a far (or long if you will) jump to 0x0700:L1 which
will load CS with the proper value. Again, the offset of L1 will be correct
whether referenced against segment 0xDC00 or 0x0700. Perhaps the easiest way
to accomplish this is to push the offset then the segment values, and execute a
16-bit far return. I think the assembler wants to generate 32-bit returns by
default, so I’d make a macro to do the 16-bit RETF, then change your “call”
code as follows (I hate to spew assembley code without testing it, but you’ll
get the idea at least):

…macro RETF16
…byte 0xCB
…endm

pushw $L1 # Push offset to branch to
pushw $0x0700 # Push segment to branch to
RETF16 # Execute 16-bit RETF

This should get you executing at L1 in your IPL which has been copied to RAM (I
hope).

So now that your IPL code is fully available whilst you move your FLASH window
around, all you have to do is copy your startup header + code, plus your image
filesystem somewhere in RAM, patch 'em, and jump on in there. That’s what
happens in main.c as far as I can tell. I’m not quite sure where the switch to
protected mode happens, apparently it’s part of a pre-boot thingie that gets
run just before the startup code.

If I’m not mistaken, since you’re jumping directly to your IPL code after the
BIOS has initialized everything for you, you could treat this whole thing the
same as a non-BIOS IPL. The main difference is that in a non-BIOS IPL, you
switch to protected mode as soon as possible then simply load the startup code
and IFS, patch it, and jump directly in where the header tells you to. There
is no pre-boot code at all, and you execute in 32-bit mode where the assembler
likes to be anyway. You can’t use the main.c that relies on the BIOS calls to
copy memory and whatnot, but those routines become trivial to implement when
you have the full 4G memory space at your disposal. Yet another option for
you, if you should so desire. (at least I think that method would work)

Hopefully this well get you further along in your project. I apologize in
advance for any inaccuracies which I may have conveyed- hopefully someone will
point them out before they cause you too much grief.

-Warren

“Warren Peece” <warren@nospam.com> wrote in message
news:90bvnt$8df$1@inn.qnx.com

| I apologize in
| advance for any inaccuracies which I may have conveyed- hopefully someone
will
| point them out before they cause you too much grief.

Here’s one of those inaccuracies I thought of at about 2:00 this morning:

| pushw $L1 # Push offset to branch to
| pushw $0x0700 # Push segment to branch to
| RETF16 # Execute 16-bit RETF

RETF pops IP first, then pops CS, so I have those two pushes backwards. You
should push the segment first, then the offset. The Devil is in the details,
eh?

-Warren “I hate when that happens” Peece

“Warren Peece” <warren@nospam.com> wrote in message
news:90bvnt$8df$1@inn.qnx.com

A general assembler note (Colin can correct me if I’m wrong > :slight_smile:> , but movw
0xDC00, %ax I believe is the same thing as movw (0xDC00), %ax. In other
words,
without the $ telling the assembler that you want the literal value 0xDC00
to
be moved into AX, it thinks it’s a memory (pointer) reference so you’re
actually getting the word value at 0xDC00 instead of 0xDC00. So what
you’ve
shown here is probably not doing what you think it should.

I should’ve mentioned that the code was hand-copied. There is a $ before the
values in
my source. Also, I do have xorw %di,%di.

Thanks, you’ve given me alot to consider. It helps to get some expert
advice. I’ll let you
know what happens.

  • Wendy

“wendy” <> wwoods@nospam.arcomcontrols.com> > wrote in message
news:90994i$jon$> 1@inn.qnx.com> …
|
| It’s an x86, MediaGX. The bios that is used can only search for a video
bios
| extension,
| so we put in a jump to the flash to find the boot loader. Since the
flash is
| paged, in order
| to copy the boot image, we have to move the entire IPL out of the page
and
| into RAM.
| The flash window is 16k.
|
| What I am using is similar to the code under the sc400_ext directory.
The
| window is at
| 0xDC000, the ipl is only about 6k so moving 8k should be more than
enough. I
| want to
| move it to 0x7000. In the cstart_int19.s file I added this right before
L1:
| as follows:
|
| mv_ipl:
| movw 0x0DC00, %ax
****** ^------ Should add a $ ("$0xDC00")
| movw %ax,%ds
| xorw %si,%si
|
| movw 0x00700,%ax
****** ^------ Should add a $ ("$0x700")
| movw %ax,%es
| movw %di,%di
****** ^------------ Should be xorw
| movw %0x02000,%cx
| rep; movsw
|
| pushl %ebp
| movl %esp,%ebp
| subl $4,%esp
| movl $0x07000, -2(%ebp)
| movl $L1, -4(%ebp)
| call -4(%ebp)
|
| The entire flash is 16M, so to be sure everything’s where it’s suppose
to be
| I
| do the following:
| mkipl -ffull -s8k -r ipl-media > ipl-media.pad
| cat ipl-media.pad .boot > media.boot
| mkipl -ffull -s640k -r media.boot > media_nto.boot
|
| I added min_size = 15360k max_size = 15360k to efs.build. Then I do:
| cat media_nto.boot efs.image > image
|
| So image is 16M and everything should be written exactly where I expect
| it to be. The ipl should be located on the first page between 0xDC000
| and 0xDE000.
|
| Okay, so maybe I don’t need too much prompting > :wink:
|
| - Wendy

Since you’ve added a jump to your code from the BIOS, we don’t have to
worry
about finding the extension signature and so on. Did you remove the 6
zero
bytes at _start in cstart_int19.s? I’m not sure what exactly they’re
doing
there in the first place, but if you do a long jump to $0xDC00:0000 bad
things™ will happen if you don’t take them out. Do you indeed perform
a far
(long) jump to that address? If not, you should. That will set CS to
0xDC00
(just keeping track of where we are).

So “mv_ipl” is going to be at DC00:0000. If you take a look at
x86bios.lnk,
you’ll see that the linker is going to assume our code segment is at
0x0000 and
is 4k (0x1000) in size. The size isn’t really important, unless you fill
up
the code segment then ld will complain and you’ll need to bump that value
up.
Note that that particular value is for code only, not data. Even though
your
segment values will never actually be 0x0000, leaving the address 0 in the
linker file will ensure that all of your offsets begin at 0, relative to
the
code segment which is what we’re after.

The code at L1 in cstart_int19.s, although undocumented, appears to be
setting
the interrupt vector segment and offset values for int 0x19 to L2. This
is
supposedly where control is transferred once the POST routines are
finished.
I’m not sure if this really applies to your case, since you inserted a
direct
jump in the BIOS itsself, your code is considered to be part of the POST
routines. This code, according to the linker, is based at segment 0x0000,
and
that’s the address that’s being stored in the interrupt vector table
(although
to actual offset for L2 is correct). When you’re executing in your FLASH
window, the segment address is wrong, and when you’re executing in RAM
after
copying to address 0x7000 it’s also wrong. You’ll want to use segment
address
$0x0700 instead of $0x0000 so all of your offsets will remain happy, if
you
should ever happen to invoke int 19h for some strange reason. Perhaps
that
whole part can be left out, I’m not sure. At any rate, with the mods as
shown
above, your IPL should be fully relocated to 0700:0000, but I think your
control transfer to L1 needs a little work.

What you want to do, is a far (or long if you will) jump to 0x0700:L1
which
will load CS with the proper value. Again, the offset of L1 will be
correct
whether referenced against segment 0xDC00 or 0x0700. Perhaps the easiest
way
to accomplish this is to push the offset then the segment values, and
execute a
16-bit far return. I think the assembler wants to generate 32-bit returns
by
default, so I’d make a macro to do the 16-bit RETF, then change your
“call”
code as follows (I hate to spew assembley code without testing it, but
you’ll
get the idea at least):

.macro RETF16
.byte 0xCB
.endm

pushw $L1 # Push offset to branch to
pushw $0x0700 # Push segment to branch to
RETF16 # Execute 16-bit RETF

This should get you executing at L1 in your IPL which has been copied to
RAM (I
hope).

So now that your IPL code is fully available whilst you move your FLASH
window
around, all you have to do is copy your startup header + code, plus your
image
filesystem somewhere in RAM, patch 'em, and jump on in there. That’s what
happens in main.c as far as I can tell. I’m not quite sure where the
switch to
protected mode happens, apparently it’s part of a pre-boot thingie that
gets
run just before the startup code.

If I’m not mistaken, since you’re jumping directly to your IPL code after
the
BIOS has initialized everything for you, you could treat this whole thing
the
same as a non-BIOS IPL. The main difference is that in a non-BIOS IPL,
you
switch to protected mode as soon as possible then simply load the startup
code
and IFS, patch it, and jump directly in where the header tells you to.
There
is no pre-boot code at all, and you execute in 32-bit mode where the
assembler
likes to be anyway. You can’t use the main.c that relies on the BIOS
calls to
copy memory and whatnot, but those routines become trivial to implement
when
you have the full 4G memory space at your disposal. Yet another option
for
you, if you should so desire. (at least I think that method would work)

Hopefully this well get you further along in your project. I apologize in
advance for any inaccuracies which I may have conveyed- hopefully someone
will
point them out before they cause you too much grief.

-Warren

I forgot to mention:

The six zeros at the begining of cstart_int19.s are replaced with 55 AA 0C
EB 01 29 (in order as it appears in the ipl) when running mkrom. If I
remember right, the 0C EB 01 29 just jumps to the start of the code, but we
can’t just insert the bytes. Aside from shoving these bytes at the begining
of the ipl, I don’t know what else mkrom does.

We do still need this, because without it the ipl doesn’t execute so IPL
does have to behave like a normal bios extension.

  • Wendy

“wendy” <wwoods@nospam.arcomcontrols.com> wrote in message
news:90gj3o$st$1@inn.qnx.com
| I forgot to mention:
|
| The six zeros at the begining of cstart_int19.s are replaced with 55 AA 0C
| EB 01 29 (in order as it appears in the ipl) when running mkrom. If I
| remember right, the 0C EB 01 29 just jumps to the start of the code, but we
| can’t just insert the bytes. Aside from shoving these bytes at the begining
| of the ipl, I don’t know what else mkrom does.
|
| We do still need this, because without it the ipl doesn’t execute so IPL
| does have to behave like a normal bios extension.
|
| - Wendy

Ahh, so you don’t have a jump hard coded into your BIOS. That’s significant,
in that you’ll have to get the int 0x19 stuff hammered in there correctly.
Also you’ll need to verify what the CS value is upon entry to your code in
FLASH, to ensure that all of the offsets you reference match up when you copy
it to RAM. Unless of course you do the copy routine as if it has no idea what
segment it’s actually in, and snag the offset of a certain label from a near
call. See ipl/lib/x86/uart.s for an example of what I’m talking about (it’s
also useful to produce debug output to a serial port). Once you know the
address of one label, you can calculate the address of any other label and use
that as your source offset from CS for moving stuff to RAM.

-Warren

You’re a saint! It looks like you’re suggestion worked. Now I can switch
pages in flash without interrupting the code exection. There’s still more
problems to tackle, but this has been giving me a headache for a while now.

Thank you so much,
Wendy

“Warren Peece” <warren@nospam.com> wrote in message
news:90e35n$fid$1@inn.qnx.com

“Warren Peece” <> warren@nospam.com> > wrote in message
news:90bvnt$8df$> 1@inn.qnx.com> …

| I apologize in
| advance for any inaccuracies which I may have conveyed- hopefully
someone
will
| point them out before they cause you too much grief.

Here’s one of those inaccuracies I thought of at about 2:00 this morning:

| pushw $L1 # Push offset to branch to
| pushw $0x0700 # Push segment to branch to
| RETF16 # Execute 16-bit RETF

RETF pops IP first, then pops CS, so I have those two pushes backwards.
You
should push the segment first, then the offset. The Devil is in the
details,
eh?

-Warren “I hate when that happens” Peece

Ahh, so you don’t have a jump hard coded into your BIOS.

You are correct. I called another engineer who works alot with this BIOS to
verify. I misunderstood the orignal explanation. The bios looks for the
video bios extension, where they added a bit in the code to enable the flash
window. Then, it finds the bios extension in the flash and returns to the
BIOS system loader. It doesn’t necessarily have to return though.

  • Wendy

“wendy” <wwoods@nospam.arcomcontrols.com> wrote in message
news:90gto3$73m$1@inn.qnx.com
| You’re a saint! It looks like you’re suggestion worked. Now I can switch
| pages in flash without interrupting the code exection. There’s still more
| problems to tackle, but this has been giving me a headache for a while now.
|
| Thank you so much,
| Wendy

St. Warren of Peece? Don’t I have to be dead first? I don’t know if I’m down
with that, I’ve always considered myself more of an eggnog stick anyway…

But you’re very welcome nonetheless. :slight_smile:

-Warren