Page 1 of 1

Weird linker script alignment decision

Posted: Sun May 19, 2013 1:26 pm
by OSwhatever
For ARM, in the standard linker script provided with CodeSourcery toolchain I see this alignment statement that is supposed to create an alignment between text and data program headers.

. = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1));

Also ARM suggest something similar.

http://infocenter.arm.com/help/index.js ... IHCFE.html

. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1));
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));

Both methods seems to produce the same result. Two program headers aligned from each other but the data begins at the same offset within the page. If I do an readelf of my example I can see this.

Code: Select all

There are 17 section headers, starting at offset 0x9ce0:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        00800000 001000 000011 00   A  0   0  1
  [ 2] .hash             HASH            00800014 001014 00044c 04   A  3   0  4
  [ 3] .dynsym           DYNSYM          00800460 001460 0008e0 10   A  4   1  4
  [ 4] .dynstr           STRTAB          00800d40 001d40 000f36 00   A  0   0  1
  [ 5] .rel.plt          REL             00801c78 002c78 0000a0 08   A  3   6  4
  [ 6] .plt              PROGBITS        00801d18 002d18 000104 04  AX  0   0  4
  [ 7] .text             PROGBITS        00801e20 002e20 005f6c 00  AX  0   0  8
  [ 8] .rodata           PROGBITS        00807d90 008d90 000658 00   A  0   0  8
  [ 9] .init_array       INIT_ARRAY      008103e8 0093e8 000004 00  WA  0   0  4
  [10] .dynamic          DYNAMIC         008103ec 0093ec 000098 08  WA  4   0  4
  [11] .got              PROGBITS        00810484 009484 00005c 04  WA  0   0  4
  [12] .data             PROGBITS        008104e0 0094e0 000718 00  WA  0   0  4
  [13] .bss              NOBITS          00810bf8 009bf8 000190 00  WA  0   0  4
  [14] .comment          PROGBITS        00000000 009bf8 000030 01  MS  0   0  1
  [15] .ARM.attributes   ARM_ATTRIBUTES  00000000 009c28 000039 00      0   0  1
  [16] .shstrtab         STRTAB          00000000 009c61 00007e 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Elf file type is EXEC (Executable file)
Entry point 0x801e9d
There are 5 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00008034 0x00000000 0x000a0 0x000a0 R E 0x4
  INTERP         0x001000 0x00800000 0x00800000 0x00011 0x00011 R   0x1
      [Requesting program interpreter: /usr/lib/ld.so.1]
  LOAD           0x001000 0x00800000 0x00800000 0x083e8 0x083e8 R E 0x1000
  LOAD           0x0093e8 0x008103e8 0x008103e8 0x00810 0x009a0 RW  0x1000
  DYNAMIC        0x0093ec 0x008103ec 0x008103ec 0x00098 0x00098 RW  0x4

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .hash .dynsym .dynstr .rel.plt .plt .text .rodata
   03     .init_array .dynamic .got .data .bss
   04     .dynamic


First gcc insist that MAXPAGESIZE = 0x10000 and not 0x1000 as I want which I provide as "-z common-page-size=4096 -z max-page-size=4096". Using COMMONPAGESIZE will give the 4096 alignment. Still I don't understand this extra offset into the data segment. Doing . = ALIGN (4096);

Code: Select all

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00008034 0x00000000 0x000a0 0x000a0 R E 0x4
  INTERP         0x001000 0x00800000 0x00800000 0x00011 0x00011 R   0x1
      [Requesting program interpreter: /usr/lib/ld.so.1]
  LOAD           0x001000 0x00800000 0x00800000 0x083e8 0x083e8 R E 0x1000
  LOAD           0x00a000 0x00809000 0x00809000 0x00810 0x009a0 RW  0x1000
  DYNAMIC        0x00a004 0x00809004 0x00809004 0x00098 0x00098 RW  0x4
This would be my desired alignment as my logic would tell me but is there something I'm missing here?

Re: Weird linker script alignment decision

Posted: Sun May 19, 2013 3:57 pm
by jnc100
ld generally tries to align sections such that (offset & (PAGESIZE - 1)) == (virtaddr & (PAGESIZE - 1)) so that the sections can be easily mapped into a virtual address space if the binary is loaded into physical memory starting on a page boundary. If each section were to have its virtual address aligned to a multiple of a page size, then its offset within the file would likewise be aligned to a multiple of a page size, and thus there would be large chunks of the on-disk layout of the binary wasted (and thus the file would be large than it needed to be). In essence, both your way and the standard way achieve the same result, however I'd imagine the binary produced by your linker script is slightly larger (as evidenced by the offset of the data section being greater in your case).

ARM also supports 64 kiB pages, and I'd imagine the reason that it defaults to a page size of 0x10000 is that Linux by default uses 64 kiB pages on ARM (although I'm guessing here).

Regards,
John.

Re: Weird linker script alignment decision

Posted: Sun May 19, 2013 4:16 pm
by OSwhatever
Ok I get it, so the reason is to save space in the .elf file. The method suggested by ARM with 4096 alignment would be

Code: Select all

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00008034 0x00000000 0x000a0 0x000a0 R E 0x4
  INTERP         0x001000 0x00800000 0x00800000 0x00011 0x00011 R   0x1
      [Requesting program interpreter: /usr/lib/ld.so.1]
  LOAD           0x001000 0x00800000 0x00800000 0x08430 0x08430 R E 0x1000
  LOAD           0x009430 0x00809430 0x00809430 0x00810 0x009a0 RW  0x1000
  DYNAMIC        0x009434 0x00809434 0x00809434 0x00098 0x00098 RW  0x4
This would waste a little bit more RAM instead rather than disk space and you have to zero the memory right before the data memory starts.

Strange decision really to prioritize non-volatile storage over RAM.

Re: Weird linker script alignment decision

Posted: Sun May 19, 2013 4:40 pm
by jnc100
There is no loss of RAM - the idea is usually to load in the entire binary then map the appropriate segments to wherever they should be in virtual memory. Therefore, you lose some virtual address space but no actual RAM. As a lot of ARM chips are in embedded systems with limited file storage, this makes sense (although you will also see this done with x86 programs).

In this example, the page at file offset 0x8000-0x9000 would be mapped to be part of both the .text and .data sections, and therefore if you zero the first bytes of the .data page you would actually overwrite the end of the .text section. If you just copy the sections to the appropriate places this is fine however.

Note that this does introduce some security issues: if a page is shared between .text (read-only, execute) and .data (read-write), this means you could potentially alter the code in the .text section. For this reason, you should properly separate the sections for OS/driver code etc, but its probably okay for a process which is running in its own address space.

Regards,
John.

Re: Weird linker script alignment decision

Posted: Sun May 19, 2013 6:53 pm
by OSwhatever
I didn't realize that possibility as I would have otherwise ruled that out as it is unsafe.

Personally, I have no problem wasting almost a page for that maximum security. Also as the program headers should be page aligned inside the file in order to benefit from this, there will be unused space because of that anyway. Well, maybe you don't have to keep the first page in memory though.

Anyway, case solved, thank you for your help.