1

Taking this simple C program

const char s1[] = "hello",
           s2[] = "there";

and compiling it using gcc -c a.c -O0 -o a.o

yields in .rodata containing the following:

'hello\x00there\x00'

, which is what I expect. Each of the strings occupy 6 bytes, for 12 bytes in total.


However if I change the 2nd string to "there s", like so:

const char s1[] = "hello",
           s2[] = "there s";

, .rodata contains the following:

'hello\x00\x00\x00there s\x00'

An extra 2 null padding bytes were added to the end of s1.

I am assuming that they were added in order to align the first string to an 8byte boundary (seeing as I'm on a 64bit platform) - though I may be wrong?

My question then arises - why wasn't that done in the first example? Why weren't 2 extra padding bytes added to the end of each string to get them to an 8byte boundary?


All examples were conducted on an amd64/linux/gcc machine.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
bool3max
  • 2,748
  • 5
  • 28
  • 57
  • I'd guess that GCC chooses to naturally-align an 8-byte object, once it is exactly 8 or maybe >= 8. You could do more experiments to see which. The x86-64 System V ABI only requires 16-byte alignment for static arrays of 16 bytes or larger, so this is just a tuning heuristic, not a requirement. Also, if you look at the `.s` asm output, you can see the actual `.p2align` directives that might have an upper limit on how much padding to insert. – Peter Cordes Jul 30 '20 at 19:20
  • The padding was inserted to align the *second* object, not the first. The first string (and both strings in your first example) is less than 8 bytes, so there’s no point in aligning it. – prl Jul 31 '20 at 09:40

1 Answers1

2
Internally, GCC calculates alignment in bits, converting to bytes for printing a .align directive. In this answer I'll use bytes for everything with the corresponding internal alignment in bits between parentheses.

At the beginning, both strings are aligned 1 byte (8 bits internally in gcc). You can see the gimple to be sure.

If you take a look to i386 porting. You will see that DATA_ALIGNMENT is defined as ix86_data_alignment. This function is used by align_variable ( in varasm.c) to align strings bigger than 8 bytes to something between 8 bytes and 32 bytes depending on their size (between 64 and 256 bits internally in gcc).

After that, you can see in assemble_variable (varasm.c) that the ASM_OUTPUT_ALIGN which print the .align is only called if the align is bigger than BITS_PER_UNIT which is 1 byte by default (8 bits internally in gcc).

You can find DATA_ALIGNMENT definition in https://github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/i386.h
You can find ix86_data_alignment in https://github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/i386.c
you can find assemble_variable and align_variable in https://github.com/gcc-mirror/gcc/blob/master/gcc/varasm.c

So if you declare a string of a size equal to or greater than 8 bytes it will be aligned. You will see a .align x with x between 8 and 32 bytes depending on the size of the string. As @Peter Cordes said, it will be more visible with the assembly.

yflelion
  • 1,698
  • 2
  • 5
  • 16
  • Cache line size is 64 bytes (`.p2align 6` / `.balign 64`), not 8 bytes (`.balign 8`). The comment you quoted doesn't explain the observed behaviour. (Presumably some other code in that function does, though, and this could be a good answer if you find the actual GCC heuristic that applies to this 8-byte case.) – Peter Cordes Sep 10 '20 at 19:29
  • I updated my answer. the comment did not explain like you said. – yflelion Sep 12 '20 at 10:12
  • I think it's confusing to talk about alignment in bits. Are you *sure* GCC internals calculate alignment in bits, not bytes? In asm, `.align` takes a value in bytes, not bits, so what GCC prints is definitely in bytes. (On x86, `.align` is a synonym for `.balign`. On some other ISAs, it's a synonym for `.p2align`. Surprised GCC ever uses it; I would recommend hand-written asm always use `.balign` or `.p2align` to remove ambiguity between 4-byte vs. 16-byte for example. But I can confirm it does use `.align`: https://godbolt.org/z/dzbcT9) – Peter Cordes Sep 12 '20 at 10:21
  • Yes i am sure: ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); in i386.c for example. I agree with you about .balign and .p2align. – yflelion Sep 12 '20 at 10:29
  • Ok, I see. Since this is an answer to an asm question (where alignment is in bytes), you should probably introduce the idea of GCC internal using alignment in bits *before* you start talking about any numbers, to make it clear everything you're talking about is GCC internals. Or you can use alignment in bytes in your answer, translated from what the GCC source code actually does, as a way to *describe* the actual result of GCC's heuristics. (With maybe just a footnote about GCC doing alignment in bits). – Peter Cordes Sep 12 '20 at 10:49
  • I hope it's clearer like that. but it is true that it would have been better if internally and in asm it was the same. I didn't want to do the translation in bytes in the event that someone decides to debug ... – yflelion Sep 12 '20 at 11:05
  • You could start you answer with "Internally, GCC calculates alignment in bits, converting to bytes for printing a `.align` directive. In this answer I'll use bytes for everything." – Peter Cordes Sep 12 '20 at 11:19