gcc inline asm embedding pointer to .rodata in .text, x86 - gcc

I'm trying to embed a pointer to a string in the code section using inline assembler. But gcc is adding a $ to the start of the symbol name, causing a link error.Here is a minimal example,
static const char str[] = "bar";
int main()
{
__asm__ __volatile__
(
"jmp 0f\n\t"
".long %0\n\t"
"0:"
:
: "i" ( str )
);
return 0;
}
building with
gcc -Wall -save-temps test.c -o test
gives the error
test.o: In function `main':
test.c:(.text+0x6): undefined reference to `$str'
looking at the .s temp file, can see the additional $ prepended to str
.file "test.c"
.section .rodata
.type str, #object
.size str, 4
str:
.string "bar"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
#APP
# 4 "test.c" 1
jmp 0f
.long $str
0:
# 0 "" 2
#NO_APP
movl $0, %eax
leave
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
Think i am doing this the correct way, as the same approach works on ppc gcc,
<clip>
b 0f
.long str
0:
</clip>
Then again, maybe it is just "luck" it works for ppc. Is the issue because $ is used as a prefix for immediates when using the AT&T synax ?
In this simple example, i can work around the issue by hardcoding the symbol name, "str", in the inline assembler, but really need it to be an input constraint to the inline assembler.
Does anyone have any ideas on how to get this working on x86 targets ?
Thanks,
- Luke

The same thing happens using clang, probably because the code generator doesn't know the operand is bing used in a .long rather than as an immediate instruction operand. You code try something like:
const char str[] = "bar";
#define string(str) __asm__ __volatile__ \
( \
"jmp 0f\n\t" \
".long " #str "\n\t" \
"0:" \
)
int main()
{
string(str);
return 0;
}
(I had to remove the "static" on str because the compiler optimized it out as not being referenced.)

Related

C assembly pushl not working for WriteFile function

i used C-assembly to call the (_GetStdHandle#4) function to get (output) handle then used (_WriteFile#20) function to write my string on console using handle that i got from (_GetStdHandle#4).
i used (pushl) in my source code for each function to pass the parameters but something's is wrong because (WriteFile)) function return error (6) which is invalid handle but the handle is valid ... so something's wrong with passing argument ... yes ... my problem is passing argument to (_WriteFile) function using (pushl) ... in this code, i used (g) for each argument because there is no reason to move the parameters to register then push the registers ... so i didn't used (r) but if i use (r), the program work without any problem (which mov the parameters to registers first then push the registers (which i want to push the parameters without moving them into the registers)
this code is show nothing and the problem is from (WriteFile) function and if i use (r) for (WriteFile) parameters, the print will be done but why i can't use "g" to not mov the parameters to registers ?
typedef void * HANDLE;
#define GetStdHandle(result, handle) \
__asm ( \
"pushl %1\n\t" \
"call _GetStdHandle#4" \
: "=a" (result) \
: "g" (handle))
#define WriteFile(result, handle, buf, buf_size, written_bytes) \
__asm ( \
"pushl $0\n\t" \
"pushl %1\n\t" \
"pushl %2\n\t" \
"pushl %3\n\t" \
"pushl %4\n\t" \
"call _WriteFile#20" \
: "=a" (result) \
: "g" (written_bytes), "g" (buf_size), "g" (buf), "g" (handle))
int main()
{
HANDLE handle;
int write_result;
unsigned long written_bytes;
GetStdHandle(handle, -11);
if(handle != INVALID_HANDLE_VALUE)
{
WriteFile(write_result, handle, "Hello", 5, & written_bytes);
}
return 0;
}
the Assembly code for this program is :
.file "main.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "Hello\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB25:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
/APP
pushl $-11
call _GetStdHandle#4
# 0 "" 2
/NO_APP
movl %eax, 12(%esp)
cmpl $-1, 12(%esp)
je L2
leal 4(%esp), %eax
/APP
pushl $0
pushl %eax
pushl $5
pushl $LC0
pushl 12(%esp)
call _WriteFile#20
# 0 "" 2
/NO_APP
movl %eax, 8(%esp)
L2:
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE25:
.ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
what is the problem ?
I would question the need for calling the WINAPI through wrappers like this rather than calling them directly. You can declare prototypes for the stdcall calling convention with
__attribute__((stdcall))
If you don't need to use inline assembly you shouldn't. GCC's inline assembly is hard to get right. Getting it wrong can make the code appear to work until one day it doesn't, especially if optimizations are enabled. David Wohlferd has a good article on why you shouldn't use inline assembly if you don't need to.
The primary problem can be seen in this section of generated code:
pushl $0
pushl %eax
pushl $5
pushl $LC0
pushl 12(%esp)
call _WriteFile#20
GCC has computed the memory operand (handle) for the first parameter as 12(%esp) . The problem is that you have altered ESP with the previous pushes and now offset 12(%esp) is no longer where handle is.
To get around this problem you can pass memory addresses through registers or as immediates (if possible). Rather than use g constraint which includes m (memory constraints), simply use ri for registers and immediates. This prevents memory operands from being generated. If you pass pointers through registers you will also need to add the "memory" clobber.
The STDCALL(WINAPI) calling convention allows a function to destroy EAX, ECX, and EDX (AKA the volatile registers). It is possible that GetStdHandle and WriteFile will clobber ECX and EDX as well as return a value in EAX. You need to ensure that ECX and EDX are listed as clobbers as well (or have a constraint that marks it as output), otherwise the compiler may assume the values in those registers are the same before and after the inline assembly blocks are completed. If they are different it could cause subtle bugs.
With these changes your code could look something like:
#define INVALID_HANDLE_VALUE (void *)-1
typedef void *HANDLE;
#define GetStdHandle(result, handle) \
__asm ( \
"pushl %1\n\t" \
"call _GetStdHandle#4" \
: "=a" (result) \
: "g" (handle) \
: "ecx", "edx")
#define WriteFile(result, handle, buf, buf_size, written_bytes) \
__asm __volatile ( \
"pushl $0\n\t" \
"pushl %1\n\t" \
"pushl %2\n\t" \
"pushl %3\n\t" \
"pushl %4\n\t" \
"call _WriteFile#20" \
: "=a" (result) \
: "ri" (written_bytes), "ri" (buf_size), "ri" (buf), "ri" (handle) \
: "memory", "ecx", "edx")
int main()
{
HANDLE handle;
int write_result;
unsigned long written_bytes;
GetStdHandle(handle, -11);
if(handle != INVALID_HANDLE_VALUE)
{
WriteFile(write_result, handle, "Hello", 5, &written_bytes);
}
return 0;
}
Notes:
I marked the WriteFile inline assembly as __volatile so that the optimizer can't remove the entire inline assembly if it thinks result isn't being used. The compiler doesn't know that a side effect of the function is that the display is updated. Mark the function volatile to prevent the inline assembly from being removed entirely.
GetStdHandle doesn't have a problem with potential memory operands because there are no further uses of constraints after the initial push %1. The problem you are encountering is only an issue when ESP has been modified (via a PUSH/POP or change to ESP directly) and there is a possible use of a memory constraint in that inline assembly afterwards.

My puts function is not working in 16-bit code

I'm writing a bootloader using GCC and a small assembler bootstrap routine. I've written a puts routine that prints a string to the display using BIOS interrupts that doesn't appear to write strings properly.
My bootstrap assembler file boot.s contains:
.code16 .section .text
.extern main
.globl start
start:
mov $0x7c0, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
jmp main
here:
hlt
jmp here
My C code in main.c is:
/*
* A 16 bit bootloader.
*/
void putchar_bios(unsigned char ch);
void set_videomode(unsigned short mode);
void puts(char str[]);
#define set_stack(ss, size) \
{ \
__asm__ __volatile__ ( \
"mov %%ax, %%ss\n" \
"mov $512, %%sp\n" : : "a" (ss), "r" (size)\
); \
}
#define set_videomode(mode) \
{ \
__asm__ __volatile__ ( \
"int $0x10\n" : : "a" (mode) \
); \
}
void putchar_bios(unsigned char ch)
{
__asm__ __volatile__ (
"int $0x10\n" : : "a" (0x0E | ch)
);
}
void puts(char *str)
{
while(*str)
putchar_bios(*str++);
}
void main()
{
set_stack(0x07C0, 512);
set_videomode(0x03);
char name[] = "0001234567890";
puts(name);
//This works fine.
// for(i=0; i<15; i++)
// putchar_bios(name[i]);
while(1);
}
I have successfully done this entirely in assembly, but now I'm trying to migrate it to GCC . I am using a cross-compiler (i386-gcc) and used -m16 flag also. I have used a custom linker script.
OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00) {
*(.text);
}
.data : SUBALIGN(0) {
*(.data);
*(.rodata);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(.COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizel = SIZEOF(.bss)>>2;
__bss_sizeb = SIZEOF(.bss);
/* Boot signature */
.sig : AT(0x7DFE) {
SHORT(0xaa55);
}
}
The script I used to compile, link and run in QEMU are:
i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/boot.s
i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/main.c
i386-elf-ld -T link.ld -o b.bin -nostdlib --nmagic boot.o main.o
dd if=b.bin of=HD.img conv=notrunc
#add some noticable garbage to second sector since I also try to read it next
echo "This is the second sector..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................." | dd seek=512 bs=1 of=HD.img
qemu-system-i386 -hda HD.img # -boot a -s -S
Why does my program not display strings via my puts function properly?
I realized I set the values of segment registers to some garbage (0x7c0) which caused this to happen. I modified my assembly file to zero out the segment registers. The code now looks like:
.code16
.section .text
.extern main
.globl start
start:
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
jmp main
here:
hlt
jmp here
I had expected that the compiler would automatically initialize the segment registers, but it was not.

gcc compilation and assemblying

I am trying to create an executable with gcc. I have two files virtualstack.c (which consists of the C-code below) and stack.s which consists of the intel x86 assembly code written in AT&T syntax (seen below the C-code). My command line command is gcc -c virtualstack.c -s stack.s, but I get two errors (line 3 in stack.s) - missing symbol name in directive and no such instruction _stack_create. I thought I have correctly declared functions from C in assembly prefixed with a underscore (_). I would be very grateful for any comments.
C code:
#include <stdio.h>
#include <stdlib.h>
extern void stack_create(void);
int main(void)
{
stack_create();
return 0;
}
Assembly code:
.global _stack_create
.type, #function
_stack_create
pushl %ebp
movl $5, %esp
movl %esp, ebp
movl $21, %edx
pushl %edx
I will try to explain you method how to investigate such cases.
1) It is always good idea to make compiler work for you. So lets start with code (lets call it assemble.c):
#include <stdio.h>
#include <stdlib.h>
/* stub stuff */
void __attribute__ ((noinline))
stack_create(void) { }
int
main(void)
{
stack_create();
return 0;
}
Now compile it to assembler with gcc -S -g0 assemble.c. stack_create function was assembled to (your results may differ, so please follow my instructions by yourself):
.text
.globl stack_create
.type stack_create, #function
stack_create:
pushq %rbp
movq %rsp, %rbp
popq %rbp
ret
.size stack_create, .-stack_create
2) Now all you need is to take this template and fill it with your stuff:
.text
.globl stack_create
.type stack_create, #function
stack_create:
pushq %rbp
movq %rsp, %rbp
;; Go and put your favorite stuff here!
pushl %ebp
movl $5, %esp
movl %esp, ebp
movl $21, %edx
pushl %edx
... etc ...
popq %rbp
ret
.size stack_create, .-stack_create
And of course make it separate .s file, say stack.s.
3) Now lets compile alltogether. Remove stub stuff from assemble.c and compile everything as:
gcc assemble.c stack.s
I got no errors. I believe you will get no errors too.
The main lesson: don't ever try to write in assembler in details like sections, function labels, etc. Compiler better knows how to do it. Use his knowledge instead.

GCC Generated ASM simplified x86 ASM? How to map?

When I compile the following code to asm in GCC on cygwin:
int scheme_entry() {
return 42;
}
using:
gcc -O3 --omit-frame-pointer -S test1.c
I get the following 'ASM' generated:
.file "test1.c"
.text
.p2align 4,,15
.globl _scheme_entry
.def _scheme_entry; .scl 2; .type 32; .endef
_scheme_entry:
movl $42, %eax
ret
But the 'MOVL' command isn't actually x86 ASM. From looking at the following lists:
http://ref.x86asm.net/geek.html#x0FA0
http://en.wikipedia.org/wiki/X86_instruction_listings
There is no MOVL command, but there is
CMOVL
CMOVLE
MOVLPS
MOVLPD
MOVLHPS
My question is - is gcc ASM "simplified ASM"? If so - how do I map it to 'real ASM'?
As mentioned by ughoavgfhw, GCC outputs AT&T syntax by default, which is different to the Intel-style syntax you seem to be expecting. This behaviour, however, is configurable: you can request it to output Intel-style as follows:
gcc -masm=intel -O3 --omit-frame-pointer -S test1.c
with the key parameter being -masm=intel.
Using this command line, the assembly output I get (with a few unnecessary lines cut out for brevity) is as follows:
scheme_entry:
mov eax, 42
ret
GCC uses AT&T syntax. One of the differences is that operand sizes can be specified using an instruction suffix, and the compiler will always use these suffixes. This is actually a mov instruction with an l suffix, which means a 32-bit operand size.

Does gcc really know how to output NASM Assembly

So I have a simple C program that loops through the args passed to main then returns:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for(i = 0; i < argc; ++i) {
fprintf(stdout, "%s\n", argv[i]);
}
return 0;
}
I wanted to see how gcc wrote out the assembly in NASM format. I was looking over the output in the .asm file and noticed that the syntax was TASM. Below is the make file and the output from gcc. Am I doing something wrong or is it that gcc does not output true NASM syntax?
all: main
main: main.o
ld -o main main.o
main.o : main.c
gcc -S -masm=intel -o main.asm main.c
nasm -f elf -g -F stabs main.asm -l main.lst
AND
.file "main.c"
.intel_syntax noprefix
.section .rodata
.LC0:
.string "%s\n"
.text
.globl main
.type main, #function
main:
push ebp
mov ebp, esp
and esp, -16
sub esp, 32
mov DWORD PTR [esp+28], 0
jmp .L2
.L3:
mov eax, DWORD PTR [esp+28]
sal eax, 2
add eax, DWORD PTR [ebp+12]
mov ecx, DWORD PTR [eax]
mov edx, OFFSET FLAT:.LC0
mov eax, DWORD PTR stdout
mov DWORD PTR [esp+8], ecx
mov DWORD PTR [esp+4], edx
mov DWORD PTR [esp], eax
call fprintf
add DWORD PTR [esp+28], 1
.L2:
mov eax, DWORD PTR [esp+28]
cmp eax, DWORD PTR [ebp+8]
jl .L3
mov eax, 0
leave
ret
.size main, .-main
.ident "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"
.section .note.GNU-stack,"",#progbits
The errors on the command line are:
[mehoggan#fedora sandbox-print_args]$ make
gcc -S -masm=intel -o main.asm main.c
nasm -f elf -g -F stabs main.asm -l main.lst
main.asm:1: error: attempt to define a local label before any non-local labels
main.asm:1: error: parser: instruction expected
main.asm:2: error: attempt to define a local label before any non-local labels
main.asm:2: error: parser: instruction expected
main.asm:3: error: attempt to define a local label before any non-local labels
main.asm:3: error: parser: instruction expected
main.asm:4: error: attempt to define a local label before any non-local labels
main.asm:5: error: attempt to define a local label before any non-local labels
main.asm:5: error: parser: instruction expected
main.asm:6: error: attempt to define a local label before any non-local labels
main.asm:7: error: attempt to define a local label before any non-local labels
main.asm:7: error: parser: instruction expected
main.asm:8: error: attempt to define a local label before any non-local labels
main.asm:8: error: parser: instruction expected
main.asm:14: error: comma, colon or end of line expected
main.asm:17: error: comma, colon or end of line expected
main.asm:19: error: comma, colon or end of line expected
main.asm:20: error: comma, colon or end of line expected
main.asm:21: error: comma, colon or end of line expected
main.asm:22: error: comma, colon or end of line expected
main.asm:23: error: comma, colon or end of line expected
main.asm:24: error: comma, colon or end of line expected
main.asm:25: error: comma, colon or end of line expected
main.asm:27: error: comma, colon or end of line expected
main.asm:29: error: comma, colon or end of line expected
main.asm:30: error: comma, colon or end of line expected
main.asm:35: error: parser: instruction expected
main.asm:36: error: parser: instruction expected
main.asm:37: error: parser: instruction expected
make: *** [main.o] Error 1
What lead me to believe that this is TASM syntax was information posted at this link:
http://rs1.szif.hu/~tomcat/win32/intro.txt
TASM coders usually have lexical difficulties with NASM because it
lacks the "ptr" keyword used extensively in TASM.
TASM uses this:
mov al, byte ptr [ds:si] or mov ax, word ptr [ds:si] or mov eax,
dword ptr [ds:si]
For NASM This simply translates into:
mov al, byte [ds:si] or mov ax, word [ds:si] or mov eax, dword
[ds:si]
NASM allows these size keywords in many places, and thus gives you a
lot of control over the generated opcodes in a unifrom way, for
example These are all valid:
push dword 123 jmp [ds: word 1234] ; these both specify the size
of the offset jmp [ds: dword 1234] ; for tricky code when
interfacing 32bit and
; 16bit segments
it can get pretty hairy, but the important thing to remember is you
can have all the control you need, when you want it.
Intel syntax means Intel syntax, not NASM syntax. MASM and TASM syntaxes are based on Intel Syntax, NASM syntax gets inspiration from Intel syntax, but it is different.
What gcc outputs is actually gas syntax using Intel syntax for individual instructions, (Assembler directives, labels et al. use gas-specific syntax)

Resources