I'm trying to create a Shared library (*.so) in ASM and I'm not sure that i do it correct...
My code is:
.section .data
.globl var1
var1:
.quad 0x012345
.section .text
.globl func1
func1:
xor %rax, %rax
# mov var1, %rcx # this is commented
ret
To compile it i run
gcc ker.s -g -fPIC -m64 -o ker.o
gcc ker.o -shared -fPIC -m64 -o libker.so
I can access variable var1 and call func1 with dlopen() and dlsym() from a program in C.
The problem is in variable var1. When i try to access it from func1, i.e. uncomment that line, the compiler generates an error:
/usr/bin/ld: ker.o: relocation R_X86_64_32S against `var1' can not be used when making a shared object; recompile with -fPIC
ker.o: could not read symbols: Bad value
collect2: ld returned 1 exit status
I don't understand. I've already compiled with -fPIC, so what's wrong?
I've already compiled with -fPIC, so what's wrong?
That part of the error message is for people who are linking compiler-generated code.
You're writing asm by hand, so as datenwolf correctly wrote, when writing a shared library in assembly, you have to take care for yourself that the code is position independent.
This means file must not contain any 32-bit absolute addresses (because relocation to an arbitrary 64-bit base is impossible). 64-bit absolute relocations are supported, but normally you should only use that for jump tables.
mov var1, %rcx uses a 32-bit absolute addressing mode. You should normally never do this, even in position-dependent x86-64 code. The normal use-cases for 32-bit absolute addresses are: putting an address into a 64-bit register withmov $var1, %edi (zero-extends into RDI)
and indexing static arrays: mov arr(,%rdx,4), %edx
mov var1(%rip), %rcx uses a RIP-relative 32-bit offset. It's the efficient way to address static data, and compilers always use this even without -fPIE or -fPIC for static/global variables.
You have basically two possibilities:
Normal library-private static data, like C compilers will make for __attribute__((visibility("hidden"))) long var1;, same as for -fno-PIC.
.data
.globl var1 # linkable from other .o files in the same shared object / library
.hidden var1 # not visible for *dynamic* linking outside the library
var1:
.quad 0x012345
.text
.globl func1
func1:
xor %eax, %eax # return 0
mov var1(%rip), %rcx
ret
full symbol-interposition-aware code like compilers generate for -fPIC.
You have to use the Global Offset Table. This is how a compiler does it, if you tell him to produce code for a shared library.
Note that this comes with a performance hit because of the additional indirection.
See Sorry state of dynamic libraries on Linux for more about symbol-interposition and the overheads it imposes on code-gen for shared libraries if you're not careful about restricting symbol visibility to allow inlining.
var1#GOTPCREL is the address of a pointer to your var1, the pointer itself is reachable with rip-relative addressing, while the content (the address of var1) is filled by the linker during loading of the library. This supports the case where the program using your library defined var1, so var1 in your library should resolve to that memory location instead of the one in the .data or .bss (or .text) of your .so.
.section .data
.globl var1
# without .hidden
var1:
.quad 0x012345
.section .text
.globl func1
func1:
xor %eax, %eax
mov var1#GOTPCREL(%rip), %rcx
mov (%rcx), %rcx
ret
See some additional information at http://www.bottomupcs.com/global_offset_tables.html
An example on the Godbolt compiler explorer of -fPIC vs. -fPIE shows the difference that symbol-interposition makes for getting the address of non-hidden global variables:
movl $x, %eax 5 bytes, -fno-pie
leaq x(%rip), %rax 7 bytes, -fPIE and hidden globals or static with -fPIC
y#GOTPCREL(%rip), %rax 7 bytes and a load instead of just ALU, -fPIC with non-hidden globals.
Actually loading always uses x(%rip), except for non-hidden / non-static vars with -fPIC where it has to get the runtime address from the GOT first, because it's not a link-time constant offset relative to the code.
Related: 32-bit absolute addresses no longer allowed in x86-64 Linux? (PIE executables).
A previous version of this answer stated that the DATA and BSS segments could move relative to TEXT when loading a dynamic library. This is incorrect, only the library base address is relocatable. RIP-relative access to other segments within the same library is guaranteed to be ok, and compilers emit code that does this. The ELF headers specify how the segments (which contain the sections) need to be loaded/mapped into memory.
I don't understand. I've already compiled with -fPIC, so what's wrong?
-fPIC is a flag concerning the creation of machine code from non-machine code, i.e. which operations to use. In the compilation stage. Assembly is not compiled, though! Each assembly mnemonic maps directly to a machine instruction, your code is not compiled. It's just transcribed into a slightly different format.
Since you're writing it in assembly, your assembly code must be position independent to be linkable into a shared library. -fPIC has not effect in your case, because it only affects code generation.
Ok, i think i found something...
First solution from drhirsch gives almost the same error but the relocation type is changed. And type is always ended with 32. Why is it? Why 64 bit program uses 32-bit relocation?
I found this from googling: http://www.technovelty.org/code/c/relocation-truncated.html
It says:
For code optimisation purposes, the default immediate size to the mov
instructions is a 32-bit value
So that's the case. I use 64-bit program but relocation is 32-bit and all i need is to force it to be 64 bit with movabs instruction.
This code is assembling and working (access to var1 from internal function func1 and from external C program via dlsym()):
.section .data
.globl var1
var1:
.quad 0x012345
.section .text
.globl func1
func1:
movabs var1, %rax # if one is symbol, other must be %rax
inc %rax
movabs %rax, var1
ret
But i'm in doubt about Global Offset Table. Must i use it, or this "direct" access is absolutely correct?
Related
I am trying to include a header file containing a macro into my main assembly file, but the compilation fails.
Below is my main.S file
#include "common.h"
BEGIN
mov $0x0E40, %ax
int $0x10
hlt
Below is my common.h file :
.macro BEGIN
LOCAL after_locals
.code16
cli
ljmp $0, $1f
1:
xor %ax, %ax
/* We must zero %ds for any data access. */
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %bp
/* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp
/* Store the initial dl to load stage 2 later on. */
mov %dl, initial_dl
jmp after_locals
initial_dl: .byte 0
after_locals:
.endm
Both files are in same directory. When I do the compilation :
$ as --32 -o main.o main.S
main.S: Assembler messages:
main.S:2: Error: no such instruction: `begin'
What am I missing? I did a little research and got this answer in SO, but its not helpful. Please help.
$ as --32 -o main.o main.S
as is just an assembler, it translates assembly source to object code. It does not run the C preprocessor which is supposed to expand #include.
(# is the comment character in GAS syntax for x86 so the line is treated as a comment if it's seen by the assembler instead of replaced by CPP)
What you can do:
Use gcc to assemble, with appropriate file suffix (.S or .sx), it will run the C preprocessor before running the assembler.
Add -v to see what commands gcc is invoking.
If your source has a different suffix, you can -x assembler-with-cpp source.asm.
If you want to see the intermediate result after preprocessing, add -save-temps. This will write a .s file with the preprocessed source.
If you want to pass down a command line option to as, you can for example -Wa,--32. However, it is better to use options which the compiler driver understands like -m32 or -m16 in the present case. The driver knows about such options, for example it will also cater for appropriate options when linking, provided you are linking with gcc -m32 ... as noted below.
Use a .include assembler directive which is handled by the assembler itself, not the C preprocessor.
Note: In case 1. adding include search paths by means of -I path might not work as expected: The compiler driver (gcc in this case) will add -I path only to the assembler's command line if it knows that it's the GNU assembler. You can tell this when the compiler is configured by configure flag --with-gnu-as.
Note: Similar applies to linking. You probably do not want to call the linker (ld by hand) unless you're making a static executable or flat binary; use gcc or g++ instead if you're making a normal executable to run on the host system. It will add many options needed for linking like multilib paths, search paths, etc. which you do not want to fiddle by hand.
(int $0x10 is a 16-bit BIOS call, though, which won't work under a modern mainstream OS, only DOS or a legacy BIOS bootloader.)
If your header file is just assembly then include with .include "file" directive in main.S. But this way of doing would insert the code the location where its included.
I'm looking for a gcc command-line flag or other settings to produce GOTOFF relocations rather than GOT relocations for my statically linked, position-independent i386 executable. More details on what I was trying below.
My source file g1.s looks like this:
extern int answer;
int get_answer1() { return answer; }
My other source file g2.s looks like this:
extern int answer;
int get_answer2() { return answer; }
I compile them with gcc -m32 -fPIE -Os -static -S -ffreestanding -fomit-frame-pointer -fno-unwind-tables -fno-asynchronous-unwind-tables g1.c for i386.
I get the following assembly output:
.file "g1.c"
.text
.globl get_answer1
.type get_answer1, #function
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer#GOT(%ecx), %eax
movl (%eax), %eax
ret
.size get_answer1, .-get_answer1
.section .text.__x86.get_pc_thunk.cx,"axG",#progbits,__x86.get_pc_thunk.cx,comdat
.globl __x86.get_pc_thunk.cx
.hidden __x86.get_pc_thunk.cx
.type __x86.get_pc_thunk.cx, #function
__x86.get_pc_thunk.cx:
movl (%esp), %ecx
ret
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",#progbits
Here is how to reproduce this behavior online with GCC 7.2: https://godbolt.org/g/XXkxJh
Instead of GOT above, I'd like to get GOTOFF, and the movl %(eax), %eax should disappear, so the assembly code for the function should look like this:
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer#GOTOFF(%ecx), %eax
ret
I have verified that this GOTOFF assembly version is what works, and the GOT version doesn't work (because it has an extra pointer indirection).
How can I convince gcc to generate the GOTOFF version? I've tried various combinations of -fPIC, -fpic, -fPIE, -fpie, -pie, -fno-plt. None of them worked, all of them made gcc produce the GOT version.
I couldn't find any i386-specific flag on https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html or any generic flag here: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
In fact, I'm getting GOTOFF relocations for "..." string literals, and I also want to get them for extern variables.
The final output is a statically linked executable in a custom binary format (for which I've written a GNU ld linker script). There is no dynamic linking and no shared libraries. The address randomization is performed by a custom loader, which is free to load the executable to any address. So I do need position-independent code. There is no per-segment memory mapping: the entire executable is loaded as is, contiguously.
All the documentation I've been able to find online talk about position-independent executables which are dynamically linked, and I wasn't able to find anything useful there.
I wasn't able to solve this with gcc -fPIE, so I solved it manually, by processing the output file.
I use gcc -Wl,-q, with an output ELF executable file containing the relocations. I post-process this ELF executable file, and I add the following assembly instructions to the beginning:
call next
next:
pop ebx
add [ebx + R0 + (after_add - next)], ebx
add [ebx + R1 + (after_add - next)], ebx
add [ebx + R2 + (after_add - next)], ebx
...
after_add:
, where R0, R1, R2 ... are the addresses of R_386_32 relocations in the ELF executable. The In use objdump -O binary prog.elf prog.bin', and nowprog.bin' contains position-independent code, because it starts with the `add [ebx + ...], ebx' instructions, which do the necessary relocations to the code when the code starts running.
Depending on the execution environment, the gcc flag -Wl,-N is needed, to make the .text section writable (the `add [ebx + ...], ebx' instructions need that).
I use gcc -S for a hello world program. What are the 5 .seh_ commands? I can't seem to find much info at all about them when I search.
.file "hi.c"
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC0:
.ascii "Hello World\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
call __main
leaq .LC0(%rip), %rcx
call puts
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (rubenvb-4.8.0) 4.8.0"
.def puts; .scl 2; .type 32; .endef
These are gas's implementation of MASM's frame handling pseudos for generating an executable's .pdata and .xdata sections (structured exception handling stuff). Also check out Raw Pseudo Operations. Apparently if your code might be in the stack during an SEH unwind operation, you are expected to use these.
I found slightly more information at https://sourceware.org/ml/binutils/2009-08/msg00193.html. This thread seems to be the original checkin to gas to add support for all the .set_* pseudo ops.
I would like to show the patch for .pdata and .xdata generation of
pe-coff targets via gas, and to get some feed-back. This patch
includes support for arm, ppc, arm, sh (3&4), mips, and x64. As for
x86 there is no OS support for runtime function information, I spared
this part. It would just increase executable size for x86 PE and there
is no real gain for this target.
Short overview:
There are at the moment three different function entry formats preset.
The first is the MIPS one. The second version is for ARM, PPC, SH3,
and SH4 mainly for Windows CE. The third is the IA64 and x64 version.
Note, the IA64 isn't implemented yet, but to find information about
it, please see specification about IA64 on
http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
The first version has just entries in the pdata section: BeginAddress,
EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress.
Each value is a pointer to the corresponding data and has size of 4
bytes.
The second variant has the following entries in the pdata section.
BeginAddress, PrologueLength (8 bits), EndAddress (22 bits),
Use-32-bit-instruction (1 bit), and Exception-Handler-Exists (1 bit).
If the FunctionLength is zero, or the Exception-Handler-Exists bit is
true, a DATA_EH block is placed directly before function entry.
The third version has a function entry block of BeginAddress (RVA),
EndAddress (RVA), and UnwindData (RVA). The description of the
prologue, excepetion-handler, and additional SEH data is stored within
the UNWIND_DATA field in the xdata section.
.seh_proc <fct_name>
This specifies, that a SEH block begins for the function <fct_name>. This is valid for all
targets.
.seh_endprologue
By this pseudo the location of the prologue end-address (taken by the current code address of the appearance of
this pseudo). Valid for all targets.
.seh_handler <handler>[,<handler-data>]
This pseudo specifies the handler function to be used. For version 2 the
handler-data field specifies the user optional data block. For version
3 the handler-data field can be a rva to user-data (for FHANDLER), if
the name is #unwind the UHANDLER unwind block is generated, and if it
is #except (or not specified at all) EHANDLER exception block is
generated.
.seh_eh
This pseudo is used for version 2 to indicate the location of the function begin in assembly. Here the PDATA_EH data is
may stored to.
.seh_32/.seh_no32
This pseudos are just used for version 2 (see above for description). At the moment it defaults to no32, if not
specified.
.seh_endproc
By this pseudo the end of the SEH block is specified.
.seh_setframe <reg>,<offset>
By this pseudo the frame-register and the offset (value between 0-240 with 16-byte
alignment) can be specified. This is just used by version 3.
.seh_stackalloc <size>
By this stack allocation in code is described for version 3.
.seh_pushreg <reg>
By this a general register push in code is described for version 3.
.seh_savereg <reg>
By this a general register save to memory in code is described for version 3.
.seh_savemm <mm>
By this a mm register save to memory in code is described for version 3.
.seh_savexmm
By this a xmm register save to memory in code is described for version 3.
.seh_pushframe
By this information about entry kind can be described for version 3.
.seh_scope <begin>,<end>,<handler>,<jump>
By this SCOPED entries for unwind or exceptions can be specified for
version 3. This is just valid for UHANDLE and EHANDLER xdata
descriptor and a global handler has to be specified. For handler and
jump arguments, names of #1,#0, and #null can be used and they are
specifying that a constant instead of a rva has to be used.
There is also some hard-core discussion of .xdata and .pdata (along with a bunch of links) at https://sourceware.org/ml/binutils/2009-04/msg00181.html.
I stopped them from being output by using:
gcc -S -fno-asynchronous-unwind-tables hi.c
so I can look that up. But I'm happy with just not having them output anymore.
They seem related to exception handling. That's all I could find.
http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/external/gpl3/binutils/dist/gas/config/obj-coff-seh.h
I run a simple piece x86-64 assembly code of hello world.
.global main
.text
main:
mov $message, %rdi
sub $8, %rsp
call puts
add $8, %rsp
ret
message:
.asciz "Hello, World"
I use gcc_4.8.2 under cygwin to compile this program under my 64-bit windows os.
gcc -o helloworld helloworld.s
but the compiler always give me the error:
/tmp/ccylxw5q.o:fake:(.text+0x3): relocation truncated to fit: R_X86_64_32S against `.text'
how to solve this problem?
Under gas (which is invoked by gcc) you need to use the movabsq mnemonic if you want to load a 64 bit immediate. Otherwise the assembler will use the 32 bit sign-extending mov, which is indicated by the relocation type of 32S as well. The final address of the message might not be represented as a 32 bit sign-extended value under certain memory layouts which is probably causing the truncation warning.
.text
.globl _start
_start:
pushq %rbp
movq %rsp,%rbp
movq $2, %rax
leaveq
retq
I'm compiling with -nostdlib:
[root# test]# gcc -nostdlib -Wall minimal.S &&./a.out
Segmentation fault
What's wrong here?
BTW,is it possible to make the entry point other names than main and _start?
As #jaquadro mentions, you can specify the entry point on the command line to the linker (or use a link script): gcc -Wall -Wextra -nostdlib -Wl,-eMyEntry minimal.S && ./a.out
The reason your program segfaults is, that since you're not using the standard library there is nowhere to return back to (retq). Instead call exit using the correct syscall (in this case it is 60, which is put into rax, the first (and only) parameter is put into rdi.
Example:
.text
.globl MyEntry
MyEntry:
# Use Syscall 60 (exit) to exit with error code 42
movq $60, %rax
movq $42, %rdi
syscall
Related question on how to perform syscalls on x86_64
You can set the entry point by passing an option to the linker
http://sca.uwaterloo.ca/coldfire/gcc-doc/docs/ld_24.html
To do this with gcc, you would do something like...
gcc all_my_other_gcc_commands -Wl,-e,start_symbol
main is different, it is not the entry point to your compiled application, although it is the function that will be called from the entry point. The entry point itself, if you're compiling C or C++ code, is defined in something like Start.S deep in the source tree of glibc, and is platform-dependent. If you're programming straight assembly, I don't know what actually goes on.