Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing / linking a flat binary using NASM + LD

I'm creating my own "toy" OS, and I've gotten to the point where I am trying to understand linking and executable formats - in particular I have a flat file binary format executable which I am loading into memory at address 0x500 and then directly calling. As an example consider the following two instructions (I know its contrived, I just wanted to include both a call and mov in my sample)

mov ax, some_label
call some_label
; some_label is at address 0x99 into the resulting binary

Up until now I have been using NASM to produce the desired output by using the org 0x500 directive with the command nasm -f bin myfile.asm. The resulting disassembly looks like this and works perfectly:

mov ax, 0x599
call 0x599

I now want to start using LD so that I can link against other objects, but after a lot of experimentation and reading up I still don't really understand whats going on enough to get reliable results.

I've gather than in order to produce similar output I need to:

  • Get NASM to output to an obj format which include symbol information suitable for linking (I've chosen ELF as it seems to be as good a format as any)
  • Get LD to link the result with the address of the .text section as 0x500 and then emit the result as a flat binary - it is the linker that ultimately decides what the various offsets get resolved to in the end binary.

So far I've tried the following:

:: Output as ELF 
nasm -f elf myfile.asm
:: Then link and output as binary with the address of .text as 0x500
ld --oformat binary -Ttext 0x500 myfile.o

However this gives me the following error (this is on Mingw):

ld: cannot perform PE operations on non PE output file

Googling that led me to this mailing list, which seems to make sense and so instead I tried the following:

:: Output as ELF
nasm -f elf myfile.asm -o myfile.o
:: Link using LD
ld myfile.o -Ttext 0x500 -s -o myfile.tmp
:: Use objdump to output as a flat binary
objcopy -O binary myfile.tmp myfile

However the resulting myfile looks like garbage:

00000000  66B8C105E8B8      mov eax,0xb8e805c1
00000006  0000              add [bx+si],al

I've tried a few variations on the above, but none of them produce what I was expecting and so right now I'm fairly confused:

  • Can anyone help me understand what it is that is going on here?
  • Also what should I be doing to give me the same sort of control over where addresses in the resulting binary are resolved to?
like image 650
Justin Avatar asked May 12 '11 14:05

Justin


1 Answers

So I figured out that I was doing multiple wrong things, the most serious of which was attempting to compile and link 16 bit code using LD (which does not support 16 bit code) and without explicitly specifying that the code was 16 bit using BITS 16 (which meant that NASM emitted 32 bit memory addresses).

This overall accounted for some of the odd error messages that LD would give me in some variations of the above situation, as well as why the dis assembly of the linked binary was garbage - I was attempting to decompile 32 bit code as 16 bit code or visa versa.

Ideally I'd like to be able to link 16 bit code, but having discovered that I can't do this with LD (and there are very few alternatives that can do this) I decided to settle with understanding whats going on when I repeat the same thing with 32 bit code. My input file:

; Address locations are now 32 bits so I must use `eax` instead of `ax`
mov eax, some_label 
call some_label
; some_label is at a different address into the resulting binary (because 
; pointers are wider the resulting machine code is larger)

I then link this using the following:

:: Output as win32 object files - makes it possible to use -r with LD.
nasm -f win myfile.asm -o myfile.o
:: Link using LD - the -r flag prevents extra un-used code (e.g. __CTOR_LIST__) being generated - see the link in my original question text
ld myfile.o -Ttext 0x500 -s -r -o myfile.tmp
:: Use objdump to output as a flat binary - -j .text ensures that just the .text section is included which keeps the output file size down
objcopy -O binary -j .text myfile.tmp myfile

When disassembling using ndisasm I need to specify -b 32 in order to get it to correctly interpret the code as 32 bit.

It took a lot of experimentation and reading up of obscure forum links but I now understand the whole thing a lot better as a result.

like image 153
Justin Avatar answered Nov 15 '22 13:11

Justin