Required Alignment of .Text Versus .Data

Required alignment of .text versus .data

The ELF ABI does not require that VirtAddr or PhysAddr be page-aligned. (I believe) it only requires that

({Virt,Phys}Addr - Offset) % PageSize == 0

That is true for both working binaries, and false for the non-working one.

Update:

I don't see how this fails for the latter.

We have: VirtAddr == 0x08048100 and Offset == 0x80 (and PageSize == 4096 == 0x1000).

(0x08048100 - 0x80) % 0x1000 == 0x80 != 0

has to agree when align == 0x10, doesn't it?

No: it has to agree with the page size (as I said earlier), or the kernel will not be able to mmap the segment.

How to set the alignment for the .data section?

The align directive works for data as well as code.

In the assembler's output file (an object file in the format the MSVC's linker can understand), it signals the required alignment of each section using metadata.

For example, if you use

section .data
align 1024*1024*2
foo: dd 1234
align 8 ; will assemble to 4 bytes of padding to reach the next multiple of 8
bar: dd 4567

The object file will have its required-alignment for that section set to 2MiB. At least that works on Linux, where I think the section in the object file inherits the highest alignment requirement seen in the source file, so very high alignments are possible.

For win32 object files, NASM even has special syntax for section alignment:

section .data data align=16 like in the example in the manual. The default is align=4 for .data in win32/win64, apparently. The maximum is 64. I don't know if align directives inside a section can increase its alignment if none is specified for -f win32 / -f win64. If not, as the manual cautions, you might be aligning wrt. the start of the section but not in an absolute sense.

ELF object files (Linux) work the same way, with each section having a required-alignment.

Your object file (hopefully) doesn't end up filled with up-to-2MiB of padding, but it might after linking if it links after something else that has a few bytes in a section that goes into the same segment as .data in the executable.

But still, knowing (or setting) the minimum alignment of the start of a section, the assembler can support align directives of any power of 2 at any point in the middle of any section. The align directive doesn't have to be at the start of a section.

Data Structure Alignment : Linker or Compiler

The answer is that both the compiler and linker0 need to understand and handle alignment requirements. The compiler is the smart one of the pair as only it understands the actual structure, stack and variable alignment rules - but it propagates some information about required alignment to the linker which also needs to respect it when generating the final executable.

The compiler takes care of a lot of runtime alignment handling and, conversely, also often relies on the fact that certain minimum alignments are met1. The existing answers here cover what the compiler does in some details.

What is missing is that the linker and loader framework also deal with alignment. Generally speaking each section has a minimum alignment attribute, and the linker writes that attribute and the loader respects it, ensuring that the section is loaded on a boundary at least as aligned as that attribute.

Different sections will have different requirements, and changes to the code can affect those directly. A simple example is global data, whether it is in the .bss, .rodata, .data or some other section. These sections will have an alignment at least as large as the largest alignment requirement for any object stored therein.

So if you have a read-only (const) global object with 64-byte alignment, your .rodata section will have a minimum alignment of 64-bytes, and the linker will ensure this requirement is met.

You can use objdump -h to see the actual alignment requirements of any object file in the Algn column. Here's a random example:

Sections:
Idx Name Size VMA LMA File off Algn Flags
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000030 0000000000400298 0000000000400298 00000298 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000288 00000000004002c8 00000000004002c8 000002c8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 00000128 0000000000400550 0000000000400550 00000550 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000036 0000000000400678 0000000000400678 00000678 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000050 00000000004006b0 00000000004006b0 000006b0 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000060 0000000000400700 0000000000400700 00000700 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000210 0000000000400760 0000000000400760 00000760 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001a 0000000000400970 0000000000400970 00000970 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000170 0000000000400990 0000000000400990 00000990 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000400b00 0000000000400b00 00000b00 2**3 CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000021e2 0000000000400b10 0000000000400b10 00000b10 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000009 0000000000402cf4 0000000000402cf4 00002cf4 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000700 0000000000402d00 0000000000402d00 00002d00 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 000000b4 0000000000403400 0000000000403400 00003400 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000003d4 00000000004034b8 00000000004034b8 000034b8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000603e10 0000000000603e10 00003e10 2**3 CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000603e18 0000000000603e18 00003e18 2**3 CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000008 0000000000603e20 0000000000603e20 00003e20 2**3 CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000001d0 0000000000603e28 0000000000603e28 00003e28 2**3 CONTENTS, ALLOC, LOAD, DATA
22 .got 00000008 0000000000603ff8 0000000000603ff8 00003ff8 2**3 CONTENTS, ALLOC, LOAD, DATA
23 .got.plt 000000c8 0000000000604000 0000000000604000 00004000 2**3 CONTENTS, ALLOC, LOAD, DATA
24 .data 00000020 00000000006040d0 00000000006040d0 000040d0 2**4 CONTENTS, ALLOC, LOAD, DATA
25 .bss 000001c8 0000000000604100 0000000000604100 000040f0 2**5 ALLOC
26 .comment 00000034 0000000000000000 0000000000000000 000040f0 2**0 CONTENTS, READONLY

The alignment requirements here vary from 2**0 (no alignment needed) to 2**5 (align on a 32-byte boundary).

Beyond the candidates you mentioned, the runtime also needs to be alignment aware. This topic is somewhat complex, but basically you can be sure that malloc and related functions return memory suitable aligned for any fundamental type (which usually just means 8-byte aligned on 64-bit systems), although things get more complicated when you are talking about over-aligned types, or C++ alignas.


0 I had originally just grouped the (compile-time) linker and (runtime) loader together as they are really two sides of the same coin (and indeed much of the linking is actually runtime linking). After looking more carefully into the loading process, however, it seems that the loader may just load the segments (sections) at their existing file offsets, automatically respecting the alignment set up by the linker.

1 Less so on platforms like x86 where unaligned access is usually allowed, but on platforms with alignment restrictions are stricter, code may actually fail if incorrect alignment is encountered.

What is the syntax of align keyword/instruction in x86 assembly?

In which assembly flavors do I use .align instead of align?

Most notably the GNU assembler (GAS) uses .align, but every assembler can have its own syntax. You should check the manual of whatever assembler you are actually using.

Do I need to write this keyword/instruction before every data object or is there a way to write it just once?

You don't need to write it before each object if you can keep track of the alignment as you go. For instance, in your example, you wrote align 16 and then assembled 4 dwords of data, which is 16 bytes. So following that data, the current address is again aligned to 16 and another align 16 would be unnecessary (though of course harmless). You could write something like

  align 16  ; align to 16 bytes = 128 bits
xor_const: dd 80000000h, 80000000h, 80000000h, 80000000h ; the 128-bit constant
another_const: dd 0deadbeef, 0deadbeefh, 0deadbeefh, 0deadbeefh

and be assured that another_const is also 16-byte aligned. Of course, if you make a typo and one of the constants ends up with more or fewer bytes than you meant, then everything else will be wrong and your program may break horribly - but maybe that's good in that you will find the bug faster.

What is a relevant documentation I can read about this?

The manual for the assembler you are using. For instance GAS manual, NASM manual, etc.

Is "align" a keyword or an instruction (or maybe something else)?

It would usually be called a directive - a command that affects the behavior of the assembler but isn't itself a machine instruction.

How to embed machine code inside ELF header? Err: Exec format error

You're not embedding source code (ASCII text), you're trying to embed machine code instructions into the ELF header.

Yours assembles to 83 bytes, but the article has an 84 byte version. It seems you changed something vs. the article's version, breaking it. Compare the binaries or source to figure out what you broke that makes the ELF headers invalid.

 (after assembling both your source and the working source from the article)
$ file tiny-84
tiny-84: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, no section header
$ file tiny-bad
tiny-bad: ELF 32-bit LSB *unknown arch 0x100* (SYSV)
$ cmp tiny-84 tiny-bad
tiny-84 tiny-bad differ: byte 9, line 1

Turns out the difference is on the 2nd data line of the ehdr:

both versions        db      0x7F, "ELF"                     ;   e_ident
-theirs db 1, 1, 1, 0, 0
+yours db 1, 1, 1, 0 ;e_indent = magic numbers, 32objects, 2compl, arch

(diff formatting hand tweaked to line them up)

So you left out one byte, misaligning all later bytes relative to their intended position in the ELF header. Obviously this breaks it, but fortunately having a known-good example to binary-compare against made it easy to find the problem.


In practice I used cmp -l tiny-84 tiny-bad to print the mismatching bytes of the binary. When it spewed a lot of differences, that plus the file size mismatch was strong evidence of a missing bytes skewing everything, rather than just 1 field having the wrong value. Also you can visually see the skew:

$ cmp -l tiny-84 tiny-bad
9 0 263
10 263 52
11 52 61
12 61 300
13 300 100
14 100 315
15 315 200
16 200 2
17 2 0
...


Related Topics



Leave a reply



Submit