How to compile a flat position-independent binary with GCC?

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.
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

iansjack wrote:That's fair enough. You just have to add the relocation information to your custom executable format and ensure that your loader does the necessary fixups.
I'm not relocating. There is no relocation. This is position-independent code. And I'm not using any global addresses (they don't work with the design of my OS) so there's nothing to fix.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How to compile a flat position-independent binary with G

Post by iansjack »

onlyonemac wrote:
iansjack wrote:That's fair enough. You just have to add the relocation information to your custom executable format and ensure that your loader does the necessary fixups.
I'm not relocating. There is no relocation. This is position-independent code. And I'm not using any global addresses (they don't work with the design of my OS) so there's nothing to fix.
I'm confused. I thought you couldn't get the simplest of test programs to compile because of relocation problems with respect to string constants. Have you now solved the problems you were having?
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

iansjack wrote:Have you now solved the problems you were having?
Yes, I removed the string constants. The final design of my operating system has no need for string constants anyway.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

I'm using this linkscript:

Code: Select all

OUTPUT_FORMAT("binary")
OUTPUT_ARCH(i386)
ENTRY(start)
phys = 0x00000000;
SECTIONS
{
  .text phys : AT(phys)
  {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(4096);
  }
  .got : AT(phys + (_GLOBAL_OFFSET_TABLE_ - code))
  {
    _GLOBAL_OFFSET_TABLE_ = .;
    *(.got)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}
This works for code that contains short switch statements, but as soon as I've got a long switch statement, the code crashes as soon as it enters the switch.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: How to compile a flat position-independent binary with G

Post by linuxyne »

OUTPUT_FORMAT("binary") with 'ld'

creates a binary which is different from that created by

OUTPUT_FORMAT("elf-i386") with 'ld' and 'objcopy -O binary'


For a sample function containing a long switch statement, the raw binary, created by the latter set of commands (i.e. raw created from elf) had proper values to jump to the correct case.

It may be a similar observation which prompted this comment - "... generating a "binary" file is generally better done using objcopy."
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

linuxyne wrote:OUTPUT_FORMAT("binary") with 'ld'

creates a binary which is different from that created by

OUTPUT_FORMAT("elf-i386") with 'ld' and 'objcopy -O binary'


For a sample function containing a long switch statement, the raw binary, created by the latter set of commands (i.e. raw created from elf) had proper values to jump to the correct case.

It may be a similar observation which prompted this comment - "... generating a "binary" file is generally better done using objcopy."
Sounds like a bit of a hackish way of generating a raw binary. Does it work for position-independent objects, and does it strip any extra data from the binary beyond the executable code (and global/constant data used by the code)?
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

My ld says target elf-i386 not found.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

I tried it with elf32-i386 and objcopy -O binary and it still doesn't work.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: How to compile a flat position-independent binary with G

Post by linuxyne »

onlyonemac wrote:My ld says target elf-i386 not found.
Apologies. I meant elf32-i386, which you correctly guessed later on.
onlyonemac wrote:I tried it with elf32-i386 and objcopy -O binary and it still doesn't work.
I tested with the commands which I picked from your posts from last year on this thread.
I did not run the binary, though - only checked the disassembly.

The commands:

Code: Select all

gcc -fPIE -ffreestanding -fno-builtin -nostdlib -nostdinc -m32 -Wl,-Bstatic -Wall -c 1.c -o 1.o
ld -o 1.bin -T 1.ld 1.o
objdump -D -b binary -m i386 1.bin
I did try a sample with -pie given to the linker, but that did not show any difference for that sample.

The source. Please excuse the casting back and forth between int and int *.

Code: Select all

int *process(int a)
{
	int s = 0;
	switch (a) {
	case 0:s = 0;break;
	case 11:s = 1;break;
	case 22:s = 2;break;
	case 33:s = 3;break;
	case 44:s = 4;break;
	case 55:s = 5;break;
	case 66:s = 6;break;
	case 77:s = 7;break;
	case 88:s = 8;break;
	case 99:s = 9;break;
	case 101:s = 10;break;
	case 112:s = 11;break;
	case 123:s = 21;break;
	case 134:s = 13;break;
	case 145:s = 14;break;
	case 156:s = 15;break;
	case 167:s = 16;break;
	case 178:s = 17;break;
	case 189:s = 18;break;
	case 190:s = 19;break;	       
	}

	return (int *)s;
}

int start(int a)
{
	int *s;
	s = process(a);
	while (1);
	return (int)s;
}
The disassembly produced by OUTPUT_FORMAT("elf32-i386") with 'ld' and 'objcopy -O binary'

Code: Select all

00000000 <.data>:
       0:       55                      push   %ebp
       1:       89 e5                   mov    %esp,%ebp
       3:       83 ec 10                sub    $0x10,%esp
       6:       e8 f5 0f 00 00          call   0x1000
       b:       05 5d 20 00 00          add    $0x205d,%eax
      10:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
      17:       81 7d 08 be 00 00 00    cmpl   $0xbe,0x8(%ebp)
      1e:       0f 87 d3 00 00 00       ja     0xf7
      24:       8b 55 08                mov    0x8(%ebp),%edx
      27:       c1 e2 02                shl    $0x2,%edx
      2a:       8b 94 02 b4 e0 ff ff    mov    -0x1f4c(%edx,%eax,1),%edx
      31:       01 d0                   add    %edx,%eax
      33:       ff e0                   jmp    *%eax
      35:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
      3c:       e9 b6 00 00 00          jmp    0xf7
      41:       c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%ebp)
      48:       e9 aa 00 00 00          jmp    0xf7
<skip>
     148:       d9 df                   (bad)
     14a:       ff                      (bad)
     14b:       ff 8f e0 ff ff 8f       decl   -0x70000020(%edi)
If process(int a) were to receive a=11 decimal, below showed that the above binary would have correctly returned 1.

Code: Select all

00000000 <.data>:
       0:       55                      push   %ebp
       1:       89 e5                   mov    %esp,%ebp
       3:       83 ec 10                sub    $0x10,%esp
       6:       e8 f5 0f 00 00          call   0x1000; returns the return addr. i.e. 0xb
       b:       05 5d 20 00 00          add    $0x205d,%eax; eax is now 0x2068
      10:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp); default return value is 0
      17:       81 7d 08 be 00 00 00    cmpl   $0xbe,0x8(%ebp); comparison added by the compiler. 
      1e:       0f 87 d3 00 00 00       ja     0xf7; jump not taken as 11 is less than 0xbe
      24:       8b 55 08                mov    0x8(%ebp),%edx; edx becomes 11
      27:       c1 e2 02                shl    $0x2,%edx; edx becomes 44=0x2c
      2a:       8b 94 02 b4 e0 ff ff    mov    -0x1f4c(%edx,%eax,1),%edx; 
      edx = (0x2c + 0x2068*1 - 0x1f4c)
            = (0x148)
            = 0xffffdfd9 from the info pasted above.
      31:       01 d0                   add    %edx,%eax; 
      eax = eax + 0xffffdfd9 
            = 0x2068 + 0xffffdfd9 
            = 0x41 after truncation.
      33:       ff e0                   jmp    *%eax; 
      jump to location 0x41, which does return the value 1, as shown in the snippet above.

The disassembly produced by OUTPUT_FORMAT("binary") with 'ld' for the same 1.o is below.
Assuming that process(11) was called,

Code: Select all

00000000 <.data>:
       0:       55                      push   %ebp
       1:       89 e5                   mov    %esp,%ebp
       3:       83 ec 10                sub    $0x10,%esp
       6:       e8 f5 0f 00 00          call   0x1000
       b:       05 61 10 00 00          add    $0x1061,%eax; eax = 0x1061 + 0xb = 0x106c
      10:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
      17:       81 7d 08 be 00 00 00    cmpl   $0xbe,0x8(%ebp); 
      1e:       0f 87 d3 00 00 00       ja     0xf7; jump not taken
      24:       8b 55 08                mov    0x8(%ebp),%edx; edx = 11 = 0xb
      27:       c1 e2 02                shl    $0x2,%edx; edx = 0x2c
      2a:       8b 94 02 1c 01 00 00    mov    0x11c(%edx,%eax,1),%edx; 
      edx = (0x2c + 0x106c*1 + 0x11c) 
            = (0x11b4)
            = the address 0x11b4 is outside the binary's size, but since one knows that we need to ultimately jump to 0x41,
               (0x11b4) should be 
            = 0x41 - eax 
            = 0x41 - 0x106c
            = 0xffffefd5, but searching the binary shows no location (such as 0x148 in the previous binary) which
               stores 0xffffefd5. Someone more experienced should be able to provide a reason for such a situation.
             
      31:       01 d0                   add    %edx,%eax
      33:       ff e0                   jmp    *%eax
      35:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
      3c:       e9 b6 00 00 00          jmp    0xf7
      41:       c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%ebp)
      48:       e9 aa 00 00 00          jmp    0xf7

I think that looking at the disassembly should provide some clues about the cause of your crash.
onlyonemac wrote:Sounds like a bit of a hackish way of generating a raw binary. Does it work for position-independent objects, and does it strip any extra data from the binary beyond the executable code (and global/constant data used by the code)?
I did try with constant strings and with global integers, and the difference, in the respective disassembly, between OUTPUT_FORMAT("elf32-i386") with objcopy and OUTPUT_FORMAT("binary") was similar to the one described above for the long switch statement. The plain, raw binary, for some reason, contained code which would not work in fetching the correct string or the correct global variable, though I have not yet spent time finding out the reason that occurs or if I made some error (missing a cmdline arg like -pie, for instance), while the objcopied binary had sensible code, which was already correctly formed when the binary was in its elf32 form.
AndrewBuckley
Member
Member
Posts: 95
Joined: Thu Jan 29, 2009 9:13 am

Re: How to compile a flat position-independent binary with G

Post by AndrewBuckley »

linuxyne wrote:

Code: Select all

int *process(int a)
{
	int s = 0;
	switch (a) {
	case 0:s = 0;break;
	case 11:s = 1;break;
	case 22:s = 2;break;
	case 33:s = 3;break;
	case 44:s = 4;break;
	case 55:s = 5;break;
	case 66:s = 6;break;
	case 77:s = 7;break;
	case 88:s = 8;break;
	case 99:s = 9;break;
	case 101:s = 10;break;
	case 112:s = 11;break;
	case 123:s = 21;break;
	case 134:s = 13;break;
	case 145:s = 14;break;
	case 156:s = 15;break;
	case 167:s = 16;break;
	case 178:s = 17;break;
	case 189:s = 18;break;
	case 190:s = 19;break;	       
	}

	return (int *)s;
}

int start(int a)
{
	int *s;
	s = process(a);
	while (1);
	return (int)s;
}
I don't know what this process function is supposed to do, but that switch statement only covers 1/11 of every input. 11 = 1 , 12 = 0 , 13 = 0 ... 21 = 0 , 22 = 2. If that's desired ignore this part. But far more important is returning a pointer to an automatic variable beyond stack scope is undefined behaviour.

Code: Select all

int process(int a)
{
	int s = 0;
	switch (a) {
	case 0:s = 0;break;
	case 11:s = 1;break;
	case 22:s = 2;break;
	case 33:s = 3;break;
	case 44:s = 4;break;
	case 55:s = 5;break;
	case 66:s = 6;break;
	case 77:s = 7;break;
	case 88:s = 8;break;
	case 99:s = 9;break;
	case 101:s = 10;break;
	case 112:s = 11;break;
	case 123:s = 21;break;
	case 134:s = 13;break;
	case 145:s = 14;break;
	case 156:s = 15;break;
	case 167:s = 16;break;
	case 178:s = 17;break;
	case 189:s = 18;break;
	case 190:s = 19;break;	       
	}

	return s;
}

int start(int a)
{
	int s = process(a);
	while (1);
	return s;
}
accomplishes the same thing, without undefined behaviour, or messy pointers
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: How to compile a flat position-independent binary with G

Post by linuxyne »

Merlin wrote: But far more important is returning a pointer to an automatic variable beyond stack scope is undefined behaviour.
The code is not returning a pointer to an automatic variable.

There is a difference between

Code: Select all

return (int *)s;
and

Code: Select all

return (int *)&s;

Edit0: Added the difference.
Also, pointers are not messy, and there's no undefined behaviour being invoked, AFAICS.
AndrewBuckley
Member
Member
Posts: 95
Joined: Thu Jan 29, 2009 9:13 am

Re: How to compile a flat position-independent binary with G

Post by AndrewBuckley »

linuxyne wrote: The code is not returning a pointer to an automatic variable.
you are correct, and I apologise. I shall take this as a sign that staying up until 2 does not leave me in the brightest of mind for programming. Time to hit the hay.
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: How to compile a flat position-independent binary with G

Post by linuxyne »

Merlin wrote: I shall take this as a sign that staying up until 2 does not leave me in the brightest of mind for programming. Time to hit the hay.
:) It's for similar reasons I did not want to change (the change you pointed out is the simpler, production-ready version, that is true) what I already had with me - much simpler to copy paste the work already done than to redo it at an hour not suitable for such endeavours.
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

It is now working with objcopy -O binary. I'm not sure why it didn't work earlier.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: How to compile a flat position-independent binary with G

Post by onlyonemac »

I'm a bit confused about the linkscript though. Would this linkscript reserve space in the final binary for the global offset table, or would the global offset table "run over" the end of the binary into unallocated memory?

Code: Select all

OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(start)
SECTIONS
{
	. = 0x00000000;
	.text : { *(.text) }
	.rodata : { *(.rodata) }
	. = ALIGN(4096);
	.data : { *(.data) }
	. = ALIGN(4096);
	.bss : { *(.bss) }
	. = ALIGN(4096);
	_GLOBAL_OFFSET_TABLE_ = .;
	. = ALIGN(4096);
}
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Post Reply