views:

343

answers:

2

The environment is Solaris on 32bit SPARC, but I think this is a more general issue with dynamic linking and/or position independent code.

I have an assembly program that I compile as position independent code and dynamically link to it from a C program. It works fine, except that I can't refer to any memory reserved by the assembly program from the assembly program. Jumping within the assembly program works fine.

I just want to read and write to memory within the assembly program, but any time I try I get segmentation fault.

I wrote this test program to debug this issue

  .section ".data"
  .global foo
foo: .word 1
  .section ".text"
  .global testprog
testprog:
  save %sp, -(92+4), %sp
  sethi %hi(foo), %o0 ! set foo, %o0
  or %o0, %lo(foo), %o0 
  call print_int
  nop
  ret
  restore

I compile this to with

as -K PIC -b

and dlopen the resulting .so in C

dlhandle = dlopen(obj_file, RTLD_NOW)
dl_testprog = dlsym(dlhandle, "testprog")

when I call dl_testprog(), it prints "4". It also prints "4" if I try to print the address of testprog or print_int. Jumping to a label and everything else works just fine. Looking at the disassembly, foo is replaced with 0x0, like it should.

Do I have to go thru _GLOBAL_OFFSET_TABLE_ or something, to be able to write to my own memory within the assembly program? If so, how do I do this? Everything I tried resulted in a segfault, and I havn't been able to find a very good guide how to do this (which leads me to believe that you are not supposed to do it. Isn't this the linkers problem anyway?).

+1  A: 

Yes, I believe you have to go through GOT to address private data. See section 9.2 here. Although NASM is x86 assembler, the general principles should be the same also on SPARC/Solaris.

Also, AT&T assemblers usually use '@got' syntax to specify relocation wrt. GOT. The exact details will be described in your assembler manual, i.e., the syntax details of NASM will not work with Solaris' assembler.

zvrba
+2  A: 

Solved it by looking at the code the C compiler outputs for PIC, which is what I should have done from the start, instead of reading manuals and random web pages.

Maybe it was obvious but indeed real address of an object in PIC (on 32b SPARC at least) is (_GLOBAL_OFFSET_TABLE_ + PC + object). And the convention is to calculate GOT address to %l7 at the beginning of a function. The details are here, except for how to actually calculate %l7.

addpc:
  add %o7, %l7, %l7 ! %o7 == addr of call == PC
  retl
   nop
testprog:
  sethi %hi(_GLOBAL_OFFSET_TABLE_-8), %l7 ! -8 = distance from call addpc
  add %l7, %lo(_GLOBAL_OFFSET_TABLE_-4), %l7 
  call addpc ! add PC to %l7
   nop
Venti
+1 for looking to the C assembly output :-) (Helped me a lot some years ago to write a real fast and optimized interrupt handler on m68k. I just started it in C grabbed the ASM output and optimized it...without programming assembler for m68k before that project).
rstevens