Search code examples
linuxgcclinkerglibcelf

Where is segment %fs for static elf images setup?


I'm trying to figure out how the %fs register is initialized when creating a elf image by hand.

The simple snippet I'd like to run is:

        .text
        nop
        movq %fs:0x28, %rax;
1:      jmp 1b

Which should read at offset 0x28 in the %fs segment. Normally this is where the stack canary is stored. Because I create the elf image by hand the %fs segment is not setup at all by my code this fails expectedly(?) .

Here is how I create the elf image:

0000000000000000 <.text>:
   0:   90                      nop
   1:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
   8:   00 00 
   a:   eb fe                   jmp    0xa

I create the .text segment via

echo 9064488b042528000000ebfe | xxd -r -p > r2.bin

Then I convert to elf:

ld -b binary -r -o raw.elf r2.bin
objcopy  --rename-section .data=.text --set-section-flags .data=alloc,code,load raw.elf

At that point raw.elf contains my instructions. I then link with ld -T raw.ld -o out.elf -M --verbose where raw.ld is:

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_entry)
PHDRS {
phdr4000000 PT_LOAD;
}
SECTIONS
{
  _entry = 0x4000000;
  .text 0x4000000 : { raw.elf (.text) } :phdr4000000
}

I can now start out.elf with gdb:

gdb --args out.elf

and set a breakpoint at 0x4000000:

(gdb)break *0x4000000
(gdb)run

The first nop can be stepped via stepi, however the stack canary read mov %fs:0x28,%rax segfaults.

I suppose that is expected given that maybe the OS is not setting up %fs. For a simple m.c: int main() { return 0; } program compiled with gcc --static m.c -o m I can read from %fs. Adding:

long can()
{
        long v = 0;
        __asm__("movq %%fs:0x28, %0;"
                : "=r"(val)::);
        return v;
}

lets me read from %fs - even though I doubt that %fs:28 is setup because ld.so is not run (it is a static image).

Question:

Can anyone point out where %fs is setup in the c runtime for static images?


Solution

  • You need to call arch_prctl with an ARCH_SET_FS argument before you can use the %fs segment prefix. You will have to allocate the backing store somewhere (brk, mmap, or an otherwise unused part of the stack).

    glibc does this in __libc_setup_tls in csu/libc-tls.c for statically linked binaries, hidden behind the TLS_INIT_TP macro.