5

How can I create a non-relocatable symbol with recent (with PIC enabled) gcc? I basically want to have the following C program printing NULL:

#include <stdio.h>

extern int mem_;

int main(void) {
  printf("%p\n", &mem_);
  return 0;
}

What I tried is a small assembler file:

    .data
    .globl  mem_
    .type mem_,@object
    .set mem_, 0

but this creates a relocatable symbol, which at runtime does not have the value 0.

Background: I am trying to get an old program running, which uses this trick to directly access (allocated) memory from Fortran as an array. Since the program has ≫10⁵ LOC, it would be not feasable to rewrite everything.

[edit] The GNU assembler manual documents an "absolute section" as

absolute section: Address 0 of this section is always "relocated" to runtime address 0. This is useful if you want to refer to an address that ld must not change when relocating. In this sense we speak of absolute addresses being "unrelocatable": they do not change during relocation.

This is probably what I would need here (right?), but I couldn't find a way enable this section. The .struct directive is documented to switch to the "absolute section"; however the following assembler does not work either:

    .globl  mem_
    .struct 0
mem_:   

The symbol appears in this case as *ABS* with objdump:

$ objdump -t memc

memc:     file format elf64-x86-64
[...]
0000000000000540 g     F .text  000000000000002b              _start
0000000000201030 g       .bss   0000000000000000              __bss_start
000000000000064a g     F .text  000000000000003c              main
0000000000000000 g       *ABS*  0000000000000000              mem_
[...]

but it is still relocated.

olebole
  • 521
  • 4
  • 17
  • 1
    Wouldn't it be a *lot* easier to make a position-dependent executable with [`-fno-pie -no-pie`](https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux)? – Peter Cordes Oct 10 '17 at 09:07
  • If this is a Fortran program, why is this tagged C and not Fortran? If you want to pass the array from C to Fortran, why not just pass `NULL`? – fuz Oct 10 '17 at 09:41
  • Fortran is just the rationale behind the question, but the problem is independent of it. Basically, I want to pass NULL, but the question is how to do it. just `mem_ = NULL` does not work, since that sets the value of the symbol to `NULL`, not the symbol itself. – olebole Oct 10 '17 at 09:48
  • 1
    Peter, I would rather not like to use `-no-pie`, since PIE is the default nowadays (f.e. on Debian), and it enables f.e. some [code hardening options](https://wiki.debian.org/Hardening#DEB_BUILD_HARDENING_PIE_.28gcc.2Fg.2B-.2B-_-fPIE_-pie.29). Also, users have the option to link their own executables, and having the need of a special flag is always a source of an error. I just want to have one symbol not relocated, why should I disable it for the rest of the executable? – olebole Oct 10 '17 at 10:05
  • Can you clarify which platform you are compiling for (and on) ? – Ajay Brahmakshatriya Oct 10 '17 at 10:20
  • Debian GNU/Linux testing (and Stretch), x86_64. But I need to have it compatible to older Linux versions as well (back to Scientific Linux 6). – olebole Oct 10 '17 at 10:22
  • Use @username to notify people when you reply. I think if you want to abuse symbols this way, you're might have to make position-dependent executables. If the Fortran code is HPC number-crunching, do you really need hardening? Is the program exposed to un-trusted input? If not, I'd take the minor performance benefit of a position-dependent executable over ASLR. I think you can safely put `-no-pie` in the Makefile so other people that build your code don't have a problem. Or maybe I'm wrong and there is a way other than @Ctx's solution that works even in a PIE. – Peter Cordes Oct 10 '17 at 21:55
  • I am afraid that I have to use `-no-pie`. However, the disadvantage is that the user (who wants to compile his own module) has to apply this as well, which is a likely cause of future problems. Also, the `-no-pie` flag does not exist on older compilers, so the compilation has to be specific for the system (and may need a change when the system gets an update). And finally, yes, the program may be exposed somewhere to un-trusted input (data). – olebole Oct 11 '17 at 06:44

1 Answers1

5

You can put your symbol in a section "fixedloc" and specify the address at link time:

#include <stdio.h>
int mem_ __attribute__ ((section ("fixedloc")));

int main (void) {
        printf("%p\n", &mem_);
        return 0;
}

And compile/link it with

gcc -O2 mem.c -o mem -Wl,--section-start=fixedloc=0x1230000

$ nm mem
...
0000000001230000 D mem
...
$ ./mem
0x1230000

Note that it will not work with location 0 on recent linux kernels if /proc/sys/vm/mmap_min_addr is not set to 0 (default is 65536) and even then, the dynamic linker chokes on it. But other addresses work flawlessly.

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • I think the OP is using `_mem` as a base address for using integers as pointers in Fortran. If they can add a constant offset in the source everywhere, that could work around using a non-zero value (address) for `_mem`, if there's no way to get the dynamic linker to accept it. But probably easier to use [`-fno-pie -no-pie`](https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux) to make a position-dependent executable if that's an option. – Peter Cordes Oct 10 '17 at 09:12
  • Unfortunately, this also does not work. I still get a different address for mem_. And I don't understand the role of the `mmap_min` address here: I will ofcourse never access the address `0` directly; `mem_` is always used in Fortran for arrays with non-zero indices. – olebole Oct 10 '17 at 09:28
  • @olebole I do not see how this can't work. Where did you deviate from the steps I showed up in the answer? If you exactly reproduce it, you will have to get the same results. – Ctx Oct 10 '17 at 09:29
  • I didn't: `$ cat memc.c #include int mem_ __attribute__ ((section ("fixedloc"))); int main(void) { printf("%p\n", &mem_); return 0; } $ gcc -O2 memc.c -o memc -Wl,--section-start=fixedloc=0x1230000 $ ./memc 0x55648e902000` That is exactly your code. BTW, sorry; I edited your code on accident :-) – olebole Oct 10 '17 at 09:43
  • @olebole: I can confirm. You have to use `-no-pie` for this to work. It fails when making a position-independent executable (like gcc does by default on some distros). If it doesn't do so by default for you, @ Ctx, use `-pie -fpie` because that's all that the `--enable-default-pie` config option does (AFAIK.) `-pie` is the linker option. See the link in my first comment. – Peter Cordes Oct 10 '17 at 10:06
  • 1
    Maybe you should mention that this will work only for ELF binaries and not for COFF (windows uses that). Since the maximum RVA for any section can be only 32 bit in COFF. Also the flag `--section-start` doesn't work with Windows linker. – Ajay Brahmakshatriya Oct 10 '17 at 10:17