OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Apr 25, 2024 8:08 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: gcc visibility
PostPosted: Sun Dec 25, 2016 12:46 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
I know exactly what I want, but I don't know how to explain it to gcc. I've googled, read all the documentation, but it does not work as it described. My problem is, I want to access internal variables from asm, which seems impossible.

So given this code:
Code:
int var=0;
void _init()
{
    var++;
}

// gcc -shared
00000000000000e9 <_init>:
  e9:   55                      push   %rbp
  ea:   48 89 e5                mov    %rsp,%rbp
  ed:   48 8b 05 1c 10 00 00    mov    0x101c(%rip),%rax        # 1110 <_DYNAMIC+0x100>
  f4:   8b 00                   mov    (%rax),%eax
  f6:   8d 50 01                lea    0x1(%rax),%edx
  f9:   48 8b 05 10 10 00 00    mov    0x1010(%rip),%rax        # 1110 <_DYNAMIC+0x100>
100:   89 10                   mov    %edx,(%rax)
102:   90                      nop
103:   5d                      pop    %rbp
104:   c3                      retq   

nm -D test.so
0000000000002000 A _edata
0000000000001130 D _end
00000000000000e9 T _init
0000000000001000 D var

The function is exported, but the variable is relocated, so it's not a surprise asm can't handle it:
Code:
.section .text
asmfunc:
    movq var, %rax
/usr/bin/ld: aaa.o: relocation R_X86_64_32S against symbol `var' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output

Telling gcc to handle symbols internally:
Code:
int var=0;
void _init()
{
    var++;
}

// gcc -shared -fvisibility=hidden
00000000000000e9 <_init>:
  e9:   55                      push   %rbp
  ea:   48 89 e5                mov    %rsp,%rbp
  ed:   8b 05 0d 0f 00 00       mov    0xf0d(%rip),%eax        # 1000 <var>
  f3:   83 c0 01                add    $0x1,%eax
  f6:   89 05 04 0f 00 00       mov    %eax,0xf04(%rip)        # 1000 <var>
  fc:   90                      nop
  fd:   5d                      pop    %rbp
  fe:   c3                      retq   

nm -D test.so
0000000000002000 A _edata
00000000000010e0 D _end

Okay, now gcc generates relocation-free, rip relative address for var, which is what I want. But relocation information disappeared! So
Code:
int var=0;
void __attribute__ ((__visibility__("default"))) _init()
{
    var++;
}

// gcc -shared -fvisibility=hidden
00000000000000e9 <_init>:
  e9:   55                      push   %rbp
  ea:   48 89 e5                mov    %rsp,%rbp
  ed:   8b 05 0d 0f 00 00       mov    0xf0d(%rip),%eax        # 1000 <var>
  f3:   83 c0 01                add    $0x1,%eax
  f6:   89 05 04 0f 00 00       mov    %eax,0xf04(%rip)        # 1000 <var>
  fc:   90                      nop
  fd:   5d                      pop    %rbp
  fe:   c3                      retq   

nm -D test.so
0000000000002000 A _edata
00000000000010e0 D _end
00000000000000e9 T _init

Seems good. Variable is not relocated and the function is exported. Now let's see asm:
Code:
.section .text
asmfunc:
    movq var, %rax
/usr/bin/ld: aaa.o: relocation R_X86_64_32S against hidden symbol `var' can not be used when making a shared object
/usr/bin/ld: final link failed: Nonrepresentable section on output

The error about "recompile with -fPIC" gone, but still not working!

I've tried all of the directives ".hidden", ".internal" etc., neither helped. I've also tried to compile without -fvisibility=hidden and adding attributes "hidden", "internal" to "var" without luck.

My question is, given a C internal variable (local, hidden whatever you call it), which is rip-relatively referenced from C, how to access that from asm?


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Sun Dec 25, 2016 4:39 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
I've found a solution, you have to add the string "(%rip)" you see in disasm to your symbol. I don't know why can't gas add it on it's own (specially when you use ".extern" directive), but hey, I got a solution :-)
Code:
movq var(%rip), %rdi

That will compile to:
Code:
108:   48 8b 3d f1 0e 00 00    mov    0xef1(%rip),%rdi        # 1000 <var>

As you can see, this version of mov does not need any more registers or relocation records, just a few bytes longer. So it's totally transparent, and since long mode only supports rip relative addressing, these two instructions are identical (as far as mnemonics concerned):
Code:
mov symbol, reg
mov symbol(%rip), reg


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Mon Jan 02, 2017 12:17 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
No, it's not working :-( :-( :-(

Let's see this example
Code:
int a = 0x123456;

void _init()
{
    a = 1;
}

Compile, and see what we've got:
Code:
$ gcc -shared -fPIC -ffreestanding -nostdlib a.c -o ac.so
$ nm -D ac.so
0000000000201018 D a
000000000020101c D __bss_start
000000000020101c D _edata
0000000000201020 D _end
00000000000002f0 T _init
$ objdump -d ac.so

ac.so:     file format elf64-x86-64


Disassembly of section .text:

00000000000002f0 <_init>:
2f0:   55                      push   %rbp
2f1:   48 89 e5                mov    %rsp,%rbp
2f4:   48 8b 05 fd 0c 20 00    mov    0x200cfd(%rip),%rax        # 200ff8 <_DYNAMIC+0xf0>
2fb:   c7 00 01 00 00 00       movl   $0x1,(%rax)
301:   90                      nop
302:   5d                      pop    %rbp
303:   c3                      retq   

As you can see, the variable "a" is exported as a dynamic symbol, and it's rip-relatively referenced. Great, exactly what I what.

Now do the same from assembly:
Code:
.global _init
.global a

.section .data
a:
    .quad 0x123456

.section .text
_init:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    a(%rip), %rax
    movl    $0x1, (%rax)
    nop
    popq    %rbp
    ret

Now compile, and...
Code:
$ gcc -shared -fPIC -ffreestanding -nostdlib a.S -o as.so
/usr/bin/ld: /tmp/cc0fePuc.o: relocation R_X86_64_PC32 against symbol `a' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

Why? Oh why on earth wants gcc to relocate "a"?!? The distance from the instruction to the data would not change under any circumstances! Just as in the C source! How come gcc know this when compiling C, but forget it on assembly?

If I remove the ".global a", it compiles fine, so the asm code is OK, but as expected there's no "a" reference in dynsym section:
Code:
$ gcc -shared -fPIC -ffreestanding -nostdlib a.S -o as.so
$ nm -D as.so
0000000000201008 D __bss_start
0000000000201008 D _edata
0000000000201008 D _end
000000000000024f T _init
$ objdump -d as.so

as.so:     file format elf64-x86-64


Disassembly of section .text:

000000000000024f <_init>:
24f:   55                      push   %rbp
250:   48 89 e5                mov    %rsp,%rbp
253:   48 8b 05 a6 0d 20 00    mov    0x200da6(%rip),%rax        # 201000 <_GLOBAL_OFFSET_TABLE_>
25a:   c7 00 01 00 00 00       movl   $0x1,(%rax)
260:   90                      nop
261:   5d                      pop    %rbp
262:   c3                      retq   


Can anybody tell me, how to tell "gas" to compile what I need? I mean, put a variable in dynsym, but reference without relocation? I've googled all night without luck...


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Mon Jan 02, 2017 12:57 am 
Offline
Member
Member

Joined: Sat Nov 07, 2015 3:12 pm
Posts: 145
HI,
A shared library code must be able to relocate all of its symbols, because said library will be used with the same binary image ( read : only one physical instance of the library) amongst processes. or many times in a single process .
Either stop building a shared library, either build a relocatable symbol in assembly.


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Mon Jan 02, 2017 1:41 am 
Offline
Member
Member
User avatar

Joined: Thu Aug 11, 2005 11:00 pm
Posts: 1110
Location: Tartu, Estonia
What happens if you run gcc on your C file with the -S option to produce assembler output, i.e., something like
Code:
gcc -shared -fPIC -ffreestanding -nostdlib -S a.c -o ac.S

How does the generated assembly file differ from the one you wrote manually? What happens if you run gcc / gas on that one to assemble it?

_________________
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Mon Jan 02, 2017 6:36 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
@Boris: please. If you knew the x86_64 architecture, you'd know that relocation is only required for absolute address translations, as x86_64 uses rip relative addressing. And I can't forget shared lib, as I'm creating a library shared among threads...

@XenOs: good thinking! Totally forgot about -S, haven't used it for years. It's using GOTPCREL, which I have tried (among others, like adding GOTOFF to _GLOBAL_OFFSET_TABLE_). Obviously I messed it up somehow, because now it's working... I'm awake and coding for more than 30 hours now, that's why. Still, it's not the solution I'm looking for.

But Boris' post and -S output make me wonder, gcc assumes the text and the data segment position can change (but very unlikely, usually the whole file is loaded at once for performance), therefore I've put it to a test: both data and instruction in the same segment. Guess what? Not working!

This is clearly a bug: I could understand if ld assumes relocation for inter-segment references by default, but not without checking the VirtAddr fields in program header first! That's what linker scripts are for! On the other hand if the reference is inside the segment (only one "load" program header), there's definitely nothing that would require relocation, yet ld demands it! That's very bad for performance.

So back to my original question, how can I use a rip relative reference (one segment only) and have an address exposed in dynsym at the same time from GAS? Which is something that both elf and x86_64 capable of, and supposed to be trivial.

ps.: I have a feeling I know what made people to abandon gcc and start creating clang, nasm etc...


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Mon Jan 02, 2017 7:28 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
Just as I expected. Given the following code (let's forget about the fact that fasm does not require that stupid "(%rip)" suffix at all):
Code:
format ELF64

public _init
public a

section '.text' executable
_init:
    mov [a], 1
    ret

section '.data' executable
  a dq 0

Compile, link and check:
Code:
$ fasm a.asm
flat assembler  version 1.71.57  (16384 kilobytes memory)
3 passes, 654 bytes.
$ ld -shared -ffreestanding -nostdlib a.o -o a.so
$ objdump -d a.so

a.so:     file format elf64-x86-64


Disassembly of section .text:

0000000000000210 <_init>:
210:   48 c7 05 e5 0d 20 00    movq   $0x1,0x200de5(%rip)        # 201000 <_GLOBAL_OFFSET_TABLE_>
217:   01 00 00 00
21b:   c3                      retq   

Disassembly of section .data:

0000000000201000 <a>:
   ...

As you can see, the instruction and the data are located in different sections, so a relocation record is generated (although they are in the same segment). Now remove the data section line, and repeat:
Code:
format ELF64

public _init
public a

section '.text' executable
_init:
    mov [a], 1
    ret

;this time same section
  a dq 0

$ fasm a.asm
flat assembler  version 1.71.57  (16384 kilobytes memory)
3 passes, 467 bytes.
$ ld -shared -ffreestanding -nostdlib a.o -o a.so
$ objdump -d a.so

a.so:     file format elf64-x86-64


Disassembly of section .text:

0000000000000210 <_init>:
210:   48 c7 05 01 00 00 00    movq   $0x1,0x1(%rip)        # 21c <a>
217:   01 00 00 00
21b:   c3                      retq   

000000000000021c <a>:
   ...

And voila! I have a shared library which is referencing it's own variable directly without relocation. So it's proven it's possible (not to mention it's more effective, no linking needed, no indirect memory reference, smaller code und so weiter).


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Tue Jan 03, 2017 10:14 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
Boris wrote:
HI,
A shared library code must be able to relocate all of its symbols, because said library will be used with the same binary image ( read : only one physical instance of the library) amongst processes. or many times in a single process .
Either stop building a shared library, either build a relocatable symbol in assembly.


Your comment about not using shared library made me wonder. Is there a way to mark symbols to export them in dynsym section? For an executable I have only symbols (_end and _edata) there which were defined in the linker script and not in the source, therefore no visibility is set for them. If there's a way to do that, then you're right, there's no need for shared object (I mean ELF type of 3).

I really want to achieve something that can be done but has not been done before. I'm 100% sure that relocation for a dynamically loaded segment is not a must. More precisely, I don't want relocations inside my library which has rip relative addressing anyway, only for inter library calls. Let's make this clear:

Suppose we have A and B libraries, and A has a function a(). Now when A calls a(), it should be a simple rip-relative call, but when B calls a(), it has to be done through GOTPLT in B. Does this make any sense to you?


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Thu Jan 05, 2017 5:56 am 
Offline
Member
Member
User avatar

Joined: Thu Aug 11, 2005 11:00 pm
Posts: 1110
Location: Tartu, Estonia
Does it help if you don't mark the symbols as global in the assembly file, but export them via a linker script, like this?

https://github.com/xenos1984/NOS/blob/m ... xports.lds

_________________
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS


Top
 Profile  
 
 Post subject: Re: gcc visibility
PostPosted: Thu Jan 05, 2017 6:05 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
XenOS wrote:
Does it help if you don't mark the symbols as global in the assembly file, but export them via a linker script, like this?

https://github.com/xenos1984/NOS/blob/m ... xports.lds

Wow, nice trick! For now, I've used two labels, one for export, and one for internal use prefixed by "my":
Code:
.global mq_recv

/* msg_t *mq_recv(from) */
mq_recv:
mymq_recv:

But that can't be used with C functions, so I'll check out!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: MichaelPetch, SemrushBot [Bot] and 232 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group