How I can add assembly code in C project? - c

I would like to compile a simple C project that has some externals functions defined in a ASM file. My main file is a C++ that calls some "extern "C"" functions that are defined in a assembly file.
When I run task "g++ build active file", I receive some warnings about the "extern" and some errors about functions defined in asm file telling "reference to my_funcions not defined".
My C++ file contains a "extern" like this:
[...]
extern "C" {
// Subrutines en ASM
void posCurScreenP1();
void moveCursorP1();
void openP1();
void getMoveP1();
void movContinuoP1();
void openContinuousP1();
void printChar_C(char c);
int clearscreen_C();
int printMenu_C();
int gotoxy_C(int row_num, int col_num);
char getch_C();
int printBoard_C(int tries);
void continue_C();
}
[...]
and my asm file contains this:
.586
.MODEL FLAT, C
; Funcions definides en C
printChar_C PROTO C, value:SDWORD
printInt_C PROTO C, value:SDWORD
clearscreen_C PROTO C
clearArea_C PROTO C, value:SDWORD, value1: SDWORD
printMenu_C PROTO C
gotoxy_C PROTO C, value:SDWORD, value1: SDWORD
getch_C PROTO C
printBoard_C PROTO C, value: DWORD
initialPosition_C PROTO C
.code
[...]
Sure I'm doing some things wrong. Could you help me?
Thanks.

hmm
so.c
extern "C" int fun ( void );
int x;
int main()
{
x=fun();
return x;
}
fun.c
int fun ( void )
{
return(5);
}
build
gcc fun.c -O2 -c -o fun.o
g++ -O2 so.cpp fun.o -o so
No errors
00000000004003e0 <main>:
4003e0: 48 83 ec 08 sub $0x8,%rsp
4003e4: e8 17 01 00 00 callq 400500 <fun>
4003e9: 89 05 45 0c 20 00 mov %eax,0x200c45(%rip) # 601034 <x>
4003ef: 48 83 c4 08 add $0x8,%rsp
4003f3: c3 retq
0000000000400500 <fun>:
400500: b8 05 00 00 00 mov $0x5,%eax
400505: c3 retq
okay so
morefun.s
.globl fun
fun:
mov $0x5,%eax
retq
build
as morefun.s -o morefun.o
g++ -O2 so.cpp morefun.o -o so
no errors,
examine
00000000004003e0 <main>:
4003e0: 48 83 ec 08 sub $0x8,%rsp
4003e4: e8 0d 01 00 00 callq 4004f6 <fun>
4003e9: 89 05 45 0c 20 00 mov %eax,0x200c45(%rip) # 601034 <x>
4003ef: 48 83 c4 08 add $0x8,%rsp
4003f3: c3 retq
0000000004004f6 <fun>:
4004f6: b8 05 00 00 00 mov $0x5,%eax
4004fb: c3 retq
Still looks good, no problem adding assembly to a C++ project by making it look like a C function.
Other path
int fun ( void )
{
return(5);
}
gnu and most other sane compilers compile to asm then call the assembler so you can just see how they do it and repeat that
gcc -O2 -S fun.c -o fun.s
as fun.s -o fun.o
cat fun.s
.file "fun.c"
.section .text.unlikely,"ax",#progbits
.LCOLDB0:
.text
.LHOTB0:
.p2align 4,,15
.globl fun
.type fun, #function
fun:
.LFB0:
.cfi_startproc
movl $5, %eax
ret
.cfi_endproc
.LFE0:
.size fun, .-fun
.section .text.unlikely
.LCOLDE0:
.text
.LHOTE0:
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609"
.section .note.GNU-stack,"",#progbits
or use save-temps.
gcc -O2 -c -save-temps fun.c -o fun.o
to see the asm generated by the compiler
It is generally more painful to try to use compiled assembly as a starting point as is, cutting and pasting sure but there is a lot of overhead and the machine generated labels that you would want to clean up rather than start from scratch. (I prefer to disassemble and work from that than to use the compiler output directly)

Related

Cannot call assembly function from C kernel

I am writing a 32-bit C kernel and I tried to call a function that I wrote in assembly.
So I wrote an assembly file containing the function myfunc and then I wrote the kernel which defined myfunc as a global variable and I linked them together so I can use it in my C code.
It worked fine, but when I tried to call it, it caused a triple fault.
However, if I use inline assembly instead,
asm("call myfunc");
It does the job.
Also notice that the disassembly for asm("call myfunc"); and for myfunc(); aren't similar:
The disassembly when using myfunc():
00000000 <kmain>:
0: f3 0f 1e fb endbr32
4: 53 push ebx
5: 83 ec 08 sub esp,0x8
8: e8 fc ff ff ff call 9 <kmain+0x9>
d: 81 c3 02 00 00 00 add ebx,0x2
13: e8 fc ff ff ff call 14 <kmain+0x14>
18: f4 hlt
19: 83 c4 08 add esp,0x8
1c: 5b pop ebx
1d: c3 ret
The disassembly when using asm("call myfunc");:
00000000 <kmain>:
0: f3 0f 1e fb endbr32
4: e8 fc ff ff ff call 5 <kmain+0x5>
9: f4 hlt
a: c3 ret
Here's how I build it:
nasm -f elf32 kernel/klink.asm -o klink.o
gcc -c kernel/main.c -m32 -nostdlib -nodefaultlibs -O1 -fno-builtin
ld -m elf_i386 -T link.ld -o KERNEL klink.o main.o
objcopy --dump-section .text=KERNEL.SYS KERNEL
link.ld:
OUTPUT_FORMAT(elf32-i386)
ENTRY(kmain)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
}
The kernel (main.c):
void kmain() {
extern void myfunc();
myfunc(); //Here it causes a triple fault.
//asm("call myfunc"); //But this works fine.
asm("hlt");
}
And this is the assembly file that contains the function (klink.asm):
[BITS 32]
SECTION .text
GLOBAL start ;define start as a global variable
GLOBAL myfunc ;also myfunc
jmp start ;jump straight into start
myfunc:
;idk what to write here... just some code :)
EXTERN kmain ;define kmain as an external variable
start:
jmp kmain ;jump into kmain

Does GCC optimize assembly source file?

I can use GCC to convert assembly code files into reallocatable files.
gcc -c source.S -o object.o -O2
Is the optimization option effective? Can I expect GCC to optimize my assembly code?
No.
GCC passes your assembly source through the preprocessor and then to the assembler. At no time are any optimisations performed.
If you don't want to hand-optimize your asm, assembly language is the wrong choice of source language for you. Perhaps consider LLVM-IR if you want something asm-like but which is actually input for an optimizing compiler. (And ISA-independent.)
To be fair, there are some binary-to-binary recompilers / optimizers that try to figure out what's implementation detail and what's important logic, and optimize accordingly. (Reading from asm source instead of machine code would also be possible; asm and machine code are easy to convert back and forth and have a nearly 1:1 mapping). But that's not what assemblers do.
An assembler's job is normally just to faithfully translate what you write into asm. Having a tool to do that is necessary for experimenting to find out what actually is faster, without the annoyance of writing actual machine code by hand.
Interestingly GAS, the GNU assembler does have some limited optimization options for x86 that aren't enabled by the GCC front-end, even if your run gcc -O2. (You can run gcc -v ... to see how the front-end invokes other programs to do the real work, with what options.)
Use gcc -Wa,-Os -O3 foo.c bar.S to enable full optimization of your C, and GAS's minor peephole optimizations for your asm. (Or -Wa,-O2, unfortunately the manual is wrong and -Os misses some of the optimizations from -O2) -Wa,... passes ... on the as command line, just like -Wl,... passes linker options through the GCC front-end.
GCC doesn't normally enable as's optimizations because it normally feeds GAS already-optimized asm.
GAS's optimizations are only for single instructions in isolation, and thus only when an instruction can be replaced by another that has exactly the same architectural effect (except for length, so the effect on RIP differs). The micro-architectural effect (performance) can also be different; that's the point of the non-size optimizations.
From the as(1) man page, so note that these are as options, not gcc options.
-O0 | -O | -O1 | -O2 | -Os
Optimize instruction encoding with smaller instruction size. -O
and -O1 encode 64-bit register load instructions with 64-bit
immediate as 32-bit register load instructions with 31-bit or
32-bits immediates, encode 64-bit register clearing instructions
with 32-bit register clearing instructions, encode 256-bit/512-bit
VEX/EVEX vector register clearing instructions with 128-bit VEX
vector register clearing instructions, encode 128-bit/256-bit EVEX
vector register load/store instructions with VEX vector register
load/store instructions, and encode 128-bit/256-bit EVEX packed
integer logical instructions with 128-bit/256-bit VEX packed
integer logical.
-O2 includes -O1 optimization plus encodes 256-bit/512-bit EVEX
vector register clearing instructions with 128-bit EVEX vector
register clearing instructions. In 64-bit mode VEX encoded
instructions with commutative source operands will also have their
source operands swapped if this allows using the 2-byte VEX prefix
form instead of the 3-byte one. Certain forms of AND as well as OR
with the same (register) operand specified twice will also be
changed to TEST.
-Os includes -O2 optimization plus encodes 16-bit, 32-bit and
64-bit register tests with immediate as 8-bit register test with
immediate. -O0 turns off this optimization.
(re: some of those VEX / EVEX operand-size and code-size optimizations: Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm? and the section near the end of my answer on How to tell the length of an x86 instruction? re: 2 vs. 3-byte VEX prefixes)
Unfortunately -O2 and -Os conflict and -Os doesn't actually include everything from -O2. You can't get it to optimize test [re]dx, 1 to test dl,1 (-Os) and optimize or al,al to test al,al (-O2).
But it's still more optimization than NASM does. (NASM's optimization is on by default, except in ancient versions; GAS's is off by default except for picking the shortest encoding without changing the mnemonic or operand names.)
test r/m32, imm8 is not encodeable so the edx version needs an imm32.
or al,al is an obsolete 8080 idiom that's not useful for x86, except sometimes on P6-family to avoid register-read stalls where intentionally re-writing the register is actually better than avoiding lengthening the dep chain.
.intel_syntax noprefix
shufps xmm0, xmm0, 0
vxorps zmm31, zmm31, zmm31
vxorps zmm1, zmm1, zmm1
vxorps ymm15, ymm15, ymm15
vpxord zmm15, zmm15, zmm15
vpxord ymm3, ymm14, ymm15
vpxord ymm3, ymm4, ymm15
vmovd xmm16, [rdi + 256] # can use EVEX scaled disp8
vmovd xmm0, [rdi + 256] # could use EVEX scaled disp8 but doesn't even with a -march enabling AVX512
xor rax, rax
or al,al
cmp dl, 0
test rdx, 1
mov rax, 1
mov rax, -1
mov rax, 0xffffffff80000000
.att_syntax
movabs $-1, %rax
movq $1, %rax
movabs $1, %rax
Assembled with gcc -g -Wa,-msse2avx -Wa,-O2 -Wa,-march=znver2+avx512dq+avx512vl -c foo.s (For some insane reason, as has -march= support for modern AMD CPU names, but for Intel only up to corei7 and some Xeon Phi, not Skylake-avx512 like GCC does. So I had to enable AVX512 manually to test that.
objdump -dwrC -Mintel -S source + disassembly
0000000000000000 <.text>:
.intel_syntax noprefix
shufps xmm0, xmm0, 0 # -msse2avx just for fun
0: c5 f8 c6 c0 00 vshufps xmm0,xmm0,xmm0,0x0
vxorps zmm31, zmm31, zmm31 # avoids triggering AVX512 frequency limit
5: 62 01 04 00 57 ff vxorps xmm31,xmm31,xmm31
vxorps zmm1, zmm1, zmm1 # shorter, using VEX
b: c5 f0 57 c9 vxorps xmm1,xmm1,xmm1
vxorps ymm15, ymm15, ymm15 # missed optimization, could vxorps xmm15, xmm0, xmm0 for a 2-byte VEX and still be a zeroing idiom
f: c4 41 00 57 ff vxorps xmm15,xmm15,xmm15
vpxord zmm15, zmm15, zmm15 # AVX512 mnemonic optimized to AVX1, same missed opt for source operands.
14: c4 41 01 ef ff vpxor xmm15,xmm15,xmm15
vpxord ymm3, ymm14, ymm15 # no optimization possible
19: c4 c1 0d ef df vpxor ymm3,ymm14,ymm15
vpxord ymm3, ymm4, ymm15 # reversed operands to allow 2-byte VEX
1e: c5 85 ef dc vpxor ymm3,ymm15,ymm4
vmovd xmm16, [rdi + 256] # uses EVEX scaled disp8 because xmm16 requires EVEX anyway
22: 62 e1 7d 08 6e 47 40 vmovd xmm16,DWORD PTR [rdi+0x100]
vmovd xmm0, [rdi + 256] # could use EVEX scaled disp8 but doesn't even with a -march enabling AVX512
29: c5 f9 6e 87 00 01 00 00 vmovd xmm0,DWORD PTR [rdi+0x100]
xor rax, rax # dropped REX prefix
31: 31 c0 xor eax,eax
or al,al
33: 84 c0 test al,al
cmp dl, 0 # optimization to test dl,dl not quite legal: different effect on AF
35: 80 fa 00 cmp dl,0x0
test rdx, 1 # partial optimization: only to 32-bit, not 8-bit
38: f7 c2 01 00 00 00 test edx,0x1
mov rax, 1
3e: b8 01 00 00 00 mov eax,0x1
mov rax, -1 # sign-extension required
43: 48 c7 c0 ff ff ff ff mov rax,0xffffffffffffffff
mov rax, 0xffffffff80000000
4a: 48 c7 c0 00 00 00 80 mov rax,0xffffffff80000000
.att_syntax
movabs $-1, %rax # movabs forces imm64, despite -O2
51: 48 b8 ff ff ff ff ff ff ff ff movabs rax,0xffffffffffffffff
movq $1, %rax # but explicit q operand size doesn't stop opt
5b: b8 01 00 00 00 mov eax,0x1
movabs $1, %rax
60: 48 b8 01 00 00 00 00 00 00 00 movabs rax,0x1
So unfortunately even explicitly enabling AVX512VL and AVX512DQ didn't get GAS to choose a shorter EVEX encoding for vmovd when an EVEX wasn't already necessary. That's perhaps still intentional: you might want some functions to use AVX512, some to avoid it. If you're using ISA-option limits to catch accidental use of ISA extensions, you would have to enable AVX512 for the whole of such a file. It might be surprising to find the assembler using EVEX where you weren't expecting.
You can manually force it with {evex} vmovd xmm0, [rdi + 256]. (Which unfortunately GCC doesn't do when compiling C, where -march=skylake-avx512 definitely does give it free reign to use AVX512 instructions everywhere.)
so.s
#define HELLO 0x5
mov $HELLO, %eax
mov $0x5,%eax
mov $0x5,%eax
mov $0x5,%eax
retq
gcc -O2 -c so.s -o so.o
objdump -d so.o
0000000000000000 <.text>:
0: b8 00 00 00 00 mov $0x0,%eax
5: b8 05 00 00 00 mov $0x5,%eax
a: b8 05 00 00 00 mov $0x5,%eax
f: b8 05 00 00 00 mov $0x5,%eax
14: c3 retq
It didnt even pre-process the define.
rename so.s to so.S
gcc -O2 -c so.S -o so.o
objdump -d so.o
0000000000000000 <.text>:
0: b8 05 00 00 00 mov $0x5,%eax
5: b8 05 00 00 00 mov $0x5,%eax
a: b8 05 00 00 00 mov $0x5,%eax
f: b8 05 00 00 00 mov $0x5,%eax
14: c3 retq
It pre-processes the define but no optimization is occurring.
Looking slightly deeper and what is being passed to as
gcc -O2 -c -save-temps so.s -o so.o
[0][as]
[1][--64]
[2][-o]
[3][so.o]
[4][so.s]
cat so.s
#define HELLO 0x5
mov $HELLO, %eax
mov $0x5,%eax
mov $0x5,%eax
mov $0x5,%eax
retq
And
gcc -O2 -c -save-temps so.S -o so.o
[0][as]
[1][--64]
[2][-o]
[3][so.o]
[4][so.s]
cat so.s
# 1 "so.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "so.S"
mov $0x5, %eax
mov $0x5,%eax
mov $0x5,%eax
mov $0x5,%eax
retq
still no optimization.
Should be more than enough to demonstrate. There are link time optimizations that you can do you have to build the objects right and then tell the linker. But I suspect it doesn't do it at a machine code level but a high level and re-generates code.
int main ( void )
{
return(5);
}
gcc -O2 so.c -save-temps -o so.o
cat so.s
.file "so.c"
.section .text.unlikely,"ax",#progbits
.LCOLDB0:
.section .text.startup,"ax",#progbits
.LHOTB0:
.p2align 4,,15
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
movl $5, %eax
ret
.cfi_endproc
.LFE0:
.size main, .-main
.section .text.unlikely
.LCOLDE0:
.section .text.startup
.LHOTE0:
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",#progbits
Using the so.S from above
gcc -flto -O2 so.S -save-temps -o so.o
cat so.s
# 1 "so.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "so.S"
mov $0x5, %eax
mov $0x5,%eax
mov $0x5,%eax
mov $0x5,%eax
retq
Using the so.c from above
gcc -flto -O2 so.c -save-temps -o so.o
cat so.s
.file "so.c"
.section .gnu.lto_.profile.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d`a`"
.string "\222L\214"
.string ""
.string "o"
.ascii "\016"
.text
.section .gnu.lto_.icf.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d"
.string "\001\016\006\004`d\330|\356\347Nv\006"
.ascii "\017\243\003I"
.text
.section .gnu.lto_.jmpfuncs.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d"
.string "\001V\006\004"
.string "\213"
.string ""
.string ""
.string "\356"
.ascii "\f"
.text
.section .gnu.lto_.inline.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d"
.string "\001\021\006\004"
.string "\21203120\001\231l\013\344\231\300b"
.string "\n\031"
.ascii "\352"
.text
.section .gnu.lto_.pureconst.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d`f`"
.string "\222\f"
.string ""
.string "X"
.ascii "\n"
.text
.section .gnu.lto_main.3f5dbe2a70110b8,"e",#progbits
.ascii "x\234\035\216\273\016\001a\020\205\347\314\277\313\026\210\236"
.ascii "B\253\3610^\301\003(<\300\376\330B\024\262\005\211\210r\223-"
.ascii "\334[\3256\n\005\2117\020\n\211NH(\0043&9\2319\231o.\016\201"
.ascii "4f\242\264\250 \202!p\270'jz\fha=\220\317\360\361bkp\b\226c\363"
.ascii "\344\216`\216\330\333nt\316\251\005Jb/Qo\210rl%\216\233\276\327"
.ascii "\r\3211L-\201\247(b\202\242^\230\241L\302\236V\237A6\025([RD"
.ascii ":s\244\364\243E5\261\337o\333&q\336e\242\273H\037y0k6W\264\362"
.ascii "\272`\033\255\337\031\275\315p\261\370\357\026\026\312\310\204"
.ascii "\333\250Wj\364\003\t\210<\r"
.text
.section .gnu.lto_.symbol_nodes.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`d\020f"
.string "\002&\206z\006\206\t\347\030#\324\256\206#\240\b"
.ascii "'\370\004\002"
.text
.section .gnu.lto_.refs.3f5dbe2a70110b8,"e",#progbits
.string "x\234ca`\004B "
.string ""
.string ""
.string "9"
.ascii "\007"
.text
.section .gnu.lto_.decls.3f5dbe2a70110b8,"e",#progbits
.string "x\234\205PMK\002Q\024\275\347\315h\222\021R-\\\270\020\027\355\222\244\020\367A\355b6A\264\013\261p\221AmZ^\377\200DB\340N\004)\320j~A\bA\021\371\007J!\241e\277#\b\354\276y3\216\320\242\013\367\343\335w\3369\367]\233#\332\372\222V%\357\213O\304\224\344\003\nM\243\\\372k\272g\211/\211\257\210;\377\340\331\302w{\370\025\031\340\035\242\201D\202\022\004xC\350\344\225\306\275\243\024\312\213\024\266\020"
.ascii "\375\263\nJ_\332\300u\317\344I`\001\211O\345\253i\006\302tB\363"
.ascii "\b\360X\303\247Se\005\337h\226\330\260\316\360\032q\177\023A"
.ascii "\224\337\337<\266\027\207\370\2502s\223\331\301T\322[#Q\224\331"
.ascii "\326\373\204\2058\321\302S\203\235+\301\266\270\247\367%\004"
.ascii "\215\376[\335\262\226\241\353\317\361\355v\266+\327|\311\254"
.ascii "\n\341\216;?\265\227x\362Z\337\214\252\234\006\234yl\244\260"
.ascii "\236\022\261\007$%\036\331\0069~\346V4\323d\327\345Q\375U\325"
.ascii "\270\247GS\032\205;\031\342\036Y=\241\224\022\273\030\002\035"
.ascii "\fd`\027\031\232\273(\344\327\362\233\024;.UJg\345\"\331'\207"
.ascii "\345Jlgw/\275\225\313Q\344\3744[\244_\320\267k~"
.text
.section .gnu.lto_.symtab.3f5dbe2a70110b8,"e",#progbits
.string "main"
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string "\260"
.string ""
.string ""
.text
.section .gnu.lto_.opts,"e",#progbits
.string "'-fmath-errno' '-fsigned-zeros' '-ftrapping-math' '-fno-trapv' '-fno-openmp' '-fno-openacc' '-mtune=generic' '-march=x86-64' '-O2' '-flto' '-fstack-protector-strong'"
.text
.comm __gnu_lto_v1,1,1
.comm __gnu_lto_slim,1,1
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",#progbits
So it still does not appear that gcc is doing any optimization removing these duplicate instructions that have no functional advantage and are basically dead code. It does show that gcc will pre-process the code if the file has the .S but not if .s (can experiment or read the docs on others .asm?). These were run on linux, gcc is gcc, binutils is binutils, the specific file names extension sensitivity may vary by target host.
The link time optimization appears to be related to the high level code as one would expect not the assembly language code. One expects the link time optimization to be based on the middle end code not back end.
We know that gcc is not an assembler it just passes it on even if it is generated from C it passes it on so it would need an assembler parser and then logic to deal with that language to then pick out things to pass on for link time optimization.
You can read more on link time optimization and see if there is a way to apply it to the assembler... I would assume not but your entire question is about how to use the tools and how they work.
Assembly language optimization isn't necessarily a thing, that is kind of the point, now there are pseudo code things for pseudo instructions that the assembler may choose an optimized implementation
ldr r0,=0x12345678
ldr r0,=0x1000
ldr r0,=0xFFFFFF12
00000000 <.text>:
0: e59f0004 ldr r0, [pc, #4] ; c <.text+0xc>
4: e3a00a01 mov r0, #4096 ; 0x1000
8: e3e000ed mvn r0, #237 ; 0xed
c: 12345678 .word 0x12345678
But that is pseudo code so the assembler that supports it is free to do whatever they want. (assemblers define the assembly language (not the target) so by definition they get to do whatever they want). On that note using a compiler as an assembler when the toolchain also has an assembler changes it into yet another assembly language as assembly language is defined by the tool. So when you allow gcc to pre-process the code you are basically using a different assembly language from as. Just like inline assembly for the compiler is yet another assembly language. At least three assembly languages per target for the gnu toolchain.

Remove stack frame setup/initilization

I have the following program:
void main1() {
((void(*)(void)) (0xabcdefabcdef)) ();
}
I create it with the following commands:
clang -fno-stack-protector -c -static -nostdlib -fpic -fpie -O0 -fno-asynchronous-unwind-tables main.c -o shellcode.o
ld shellcode.o -o shellcode -S -static -dylib -e main1 -order_file order.txt
gobjcopy -O binary --only-section=.text shellcode shellcode.output
The assembly looks like the following:
//
// ram
// ram: 00000000-00000011
//
**************************************************************
* FUNCTION *
**************************************************************
undefined FUN_00000000()
undefined AL:1 <RETURN>
FUN_00000000
00000000 55 PUSH RBP
00000001 48 89 e5 MOV RBP,RSP
00000004 48 b8 ef MOV RAX,0xabcdefabcdef
cd ab ef
cd ab 00 00
0000000e ff d0 CALL RAX
00000010 5d POP RBP
00000011 c3 RET
How do I get clang to remove the PUSH RBP, MOV RBP,RSP and POP RBP instructions as they are unnecessary?
I can do this if I write the program in assembly with the following lines:
.globl start
start:
movq $0xabcdefabcdef, %rax
call *%rax
ret
and with the following build commands:
clang -static -nostdlib main.S -o crashme.o
gobjcopy -O binary --only-section=.text crashme.o crashme.output
and the resulting assembly:
//
// ram
// ram: 00000000-0000000c
//
assume DF = 0x0 (Default)
00000000 48 b8 ef MOV RAX,0xabcdefabcdef
cd ab ef
cd ab 00 00
0000000a ff d0 CALL RAX
0000000c c3 RET
but I would much rather write C code instead of assembly.
You forgot to enable optimization. Any optimization level like -O3 enables -fomit-frame-pointer.
It will also optimize the tailcall into a jmp instead of call/ret though. If you need to avoid that for some reason, maybe you can use -fomit-frame-pointer at the default -O0.
For shellcode you might want -Os to optimize for code size. Or even clang's -Oz; that will have a side-effect of avoiding some 0 bytes in the machine code by using push imm8 / pop reg to put small constants in registers, instead of mov reg, imm32.

Is there a GCC version of the NASM ORG instruction?

I'm currently making an OS, and when I tried to add C support, I ran into a bit of a problem... In assembly, each program on my OS starts with ORG 32768 (the NASM compiler preprocessor instruction for offsetting the origin of the code), but I can't seem to find anything on a way to do this using the GCC compiler for C. So, my question is, how would one achieve this (offsetting the code's origin) in C using GCC? (and yes, I have looked it up before asking, even checked GNU's official GCC's C preprocessor documentation)
ORG and .ORG go back to the days when you wrote programs in assembly and didnt necessarily need a linker.
The gnu tools dont support it AFAIK.
start.s
.globl _start
_start:
mov $0xA000,%rsp
callq fun
jmp .
fun.c
unsigned int fun ( void )
{
return(7);
}
fun.ld
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x2000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.data : { *(.data*) } > ram
.bss : { *(.bss*) } > ram
}
build commands
as start.s -o start.o
gcc -O2 -nostdlib -nostartfiles -ffreestanding -c fun.c -o fun.o
ld -T fun.ld start.o fun.o -o fun
which produces this program:
0000000000008000 <_start>:
8000: 48 c7 c4 00 a0 00 00 mov $0xa000,%rsp
8007: e8 04 00 00 00 callq 8010 <fun>
800c: eb fe jmp 800c <_start+0xc>
800e: 66 90 xchg %ax,%ax
0000000000008010 <fun>:
8010: b8 07 00 00 00 mov $0x7,%eax
8015: c3 retq
I used an entry point of 0x8000 (32768).
If by gcc you meant the gnu tools and just wanted to do assembly language then that makes it a bit simpler, you only need the binutils package not gcc. But you still need the linker and use the ORIGIN in the very simpler linker script example above where you would have used .ORG inline with the assembly.
start.s
.globl _start
_start:
mov $0xA000,%rsp
mov $0x7,%eax
add $0x1,%eax
jmp .
same linker script as above
as start.s -o start.o
ld -T fun.ld start.o -o fun
producing
0000000000008000 <_start>:
8000: 48 c7 c4 00 a0 00 00 mov $0xa000,%rsp
8007: b8 07 00 00 00 mov $0x7,%eax
800c: 83 c0 01 add $0x1,%eax
800f: eb fe jmp 800f <_start+0xf>

Using GCC to produce readable assembly?

I was wondering how to use GCC on my C source file to dump a mnemonic version of the machine code so I could see what my code was being compiled into. You can do this with Java but I haven't been able to find a way with GCC.
I am trying to re-write a C method in assembly and seeing how GCC does it would be a big help.
If you compile with debug symbols (add -g to your GCC command line, even if you're also using -O31),
you can use objdump -S to produce a more readable disassembly interleaved with C source.
>objdump --help
[...]
-S, --source Intermix source code with disassembly
-l, --line-numbers Include line numbers and filenames in output
objdump -drwC -Mintel is nice:
-r shows symbol names on relocations (so you'd see puts in the call instruction below)
-R shows dynamic-linking relocations / symbol names (useful on shared libraries)
-C demangles C++ symbol names
-w is "wide" mode: it doesn't line-wrap the machine-code bytes
-Mintel: use GAS/binutils MASM-like .intel_syntax noprefix syntax instead of AT&T
-S: interleave source lines with disassembly.
You could put something like alias disas="objdump -drwCS -Mintel" in your ~/.bashrc. If not on x86, or if you like AT&T syntax, omit -Mintel.
Example:
> gcc -g -c test.c
> objdump -d -M intel -S test.o
test.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
#include <stdio.h>
int main(void)
{
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 10 sub esp,0x10
puts("test");
9: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
10: e8 fc ff ff ff call 11 <main+0x11>
return 0;
15: b8 00 00 00 00 mov eax,0x0
}
1a: c9 leave
1b: c3 ret
Note that this isn't using -r so the call rel32=-4 isn't annotated with the puts symbol name. And looks like a broken call that jumps into the middle of the call instruction in main. Remember that the rel32 displacement in the call encoding is just a placeholder until the linker fills in a real offset (to a PLT stub in this case, unless you statically link libc).
Footnote 1: Interleaving source can be messy and not very helpful in optimized builds; for that, consider https://godbolt.org/ or other ways of visualizing which instructions go with which source lines. In optimized code there's not always a single source line that accounts for an instruction but the debug info will pick one source line for each asm instruction.
If you give GCC the flag -fverbose-asm, it will
Put extra commentary information in the generated assembly code to make it more readable.
[...] The added comments include:
information on the compiler version and command-line options,
the source code lines associated with the assembly instructions, in the form FILENAME:LINENUMBER:CONTENT OF LINE,
hints on which high-level expressions correspond to the various assembly instruction operands.
Use the -S (note: capital S) switch to GCC, and it will emit the assembly code to a file with a .s extension. For example, the following command:
gcc -O2 -S foo.c
will leave the generated assembly code on the file foo.s.
Ripped straight from http://www.delorie.com/djgpp/v2faq/faq8_20.html (but removing erroneous -c)
Using the -S switch to GCC on x86 based systems produces a dump of AT&T syntax, by default, which can be specified with the -masm=att switch, like so:
gcc -S -masm=att code.c
Whereas if you'd like to produce a dump in Intel syntax, you could use the -masm=intel switch, like so:
gcc -S -masm=intel code.c
(Both produce dumps of code.c into their various syntax, into the file code.s respectively)
In order to produce similar effects with objdump, you'd want to use the --disassembler-options= intel/att switch, an example (with code dumps to illustrate the differences in syntax):
$ objdump -d --disassembler-options=att code.c
080483c4 <main>:
80483c4: 8d 4c 24 04 lea 0x4(%esp),%ecx
80483c8: 83 e4 f0 and $0xfffffff0,%esp
80483cb: ff 71 fc pushl -0x4(%ecx)
80483ce: 55 push %ebp
80483cf: 89 e5 mov %esp,%ebp
80483d1: 51 push %ecx
80483d2: 83 ec 04 sub $0x4,%esp
80483d5: c7 04 24 b0 84 04 08 movl $0x80484b0,(%esp)
80483dc: e8 13 ff ff ff call 80482f4 <puts#plt>
80483e1: b8 00 00 00 00 mov $0x0,%eax
80483e6: 83 c4 04 add $0x4,%esp
80483e9: 59 pop %ecx
80483ea: 5d pop %ebp
80483eb: 8d 61 fc lea -0x4(%ecx),%esp
80483ee: c3 ret
80483ef: 90 nop
and
$ objdump -d --disassembler-options=intel code.c
080483c4 <main>:
80483c4: 8d 4c 24 04 lea ecx,[esp+0x4]
80483c8: 83 e4 f0 and esp,0xfffffff0
80483cb: ff 71 fc push DWORD PTR [ecx-0x4]
80483ce: 55 push ebp
80483cf: 89 e5 mov ebp,esp
80483d1: 51 push ecx
80483d2: 83 ec 04 sub esp,0x4
80483d5: c7 04 24 b0 84 04 08 mov DWORD PTR [esp],0x80484b0
80483dc: e8 13 ff ff ff call 80482f4 <puts#plt>
80483e1: b8 00 00 00 00 mov eax,0x0
80483e6: 83 c4 04 add esp,0x4
80483e9: 59 pop ecx
80483ea: 5d pop ebp
80483eb: 8d 61 fc lea esp,[ecx-0x4]
80483ee: c3 ret
80483ef: 90 nop
godbolt is a very useful tool, they list only has C++ compilers but you can use -x c flag in order to get it treat the code as C. It will then generate an assembly listing for your code side by side and you can use the Colourise option to generate colored bars to visually indicate which source code maps to the generated assembly. For example the following code:
#include <stdio.h>
void func()
{
printf( "hello world\n" ) ;
}
using the following command line:
-x c -std=c99 -O3
and Colourise would generate the following:
Did you try gcc -S -fverbose-asm -O source.c then look into the generated source.s assembler file ?
The generated assembler code goes into source.s (you could override that with -o assembler-filename ); the -fverbose-asm option asks the compiler to emit some assembler comments "explaining" the generated assembler code. The -O option asks the compiler to optimize a bit (it could optimize more with -O2 or -O3).
If you want to understand what gcc is doing try passing -fdump-tree-all but be cautious: you'll get hundreds of dump files.
BTW, GCC is extensible thru plugins or with MELT (a high level domain specific language to extend GCC; which I abandoned in 2017)
You can use gdb for this like objdump.
This excerpt is taken from http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64
Here is an example showing mixed source+assembly for Intel x86:
(gdb) disas /m main
Dump of assembler code for function main:
5 {
0x08048330 : push %ebp
0x08048331 : mov %esp,%ebp
0x08048333 : sub $0x8,%esp
0x08048336 : and $0xfffffff0,%esp
0x08048339 : sub $0x10,%esp
6 printf ("Hello.\n");
0x0804833c : movl $0x8048440,(%esp)
0x08048343 : call 0x8048284
7 return 0;
8 }
0x08048348 : mov $0x0,%eax
0x0804834d : leave
0x0804834e : ret
End of assembler dump.
Use the -S (note: capital S) switch to GCC, and it will emit the assembly code to a file with a .s extension. For example, the following command:
gcc -O2 -S -c foo.c
I haven't given a shot to gcc, but in case of g++, the command below works for me.
-g for debug build
-Wa,-adhln are passed to assembler for listing with source code
g++ -g -Wa,-adhln src.cpp
For risc-v dissasembly, these flags are nice:
riscv64-unknown-elf-objdump -d -S -l --visualize-jumps --disassembler-color=color --inlines
-d: disassemble, most basic flag
-S: intermix source. Note: must use -g flag while compiling
-l: line numbers
--visualize-jumps: fancy arrows, not too useful but why not. Sometimes get's too messy and actually makes reading the source harder. Taken from Peter Cordes's comment: --visualize-jumps=coloris also an option, to use different colors for different arrows
--disassembler-color=color: give the disassembly some color
--inlines: print out inlines
Maybe usefull:
-M numeric: Use numeric reg names instead of abi names, useful if you are doing cpu dev and don't know the abi names by heart
-M no-aliases: don't use psudoinstructions like li and call
Example:
main.o:
#include <stdio.h>
#include <stdint.h>
static inline void example_inline(const char* str) {
for (int i = 0; str[i] != 0; i++)
putchar(str[i]);
}
int main() {
printf("Hello world");
example_inline("Hello! I am inlined");
return 0;
}
I recommend to use -O0 if you want intermix sources. Intermix sources becomes very messy if using -O2.
Command:
riscv64-unknown-elf-gcc main.c -c -O0 -g
riscv64-unknown-elf-objdump -d -S -l --disassembler-color=color --inlines main.o
Dissasembly:
main.o: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <example_inline>:
example_inline():
/Users/cyao/test/main.c:4
#include <stdio.h>
#include <stdint.h>
static inline void example_inline(const char* str) {
0: 7179 addi sp,sp,-48
2: f406 sd ra,40(sp)
4: f022 sd s0,32(sp)
6: 1800 addi s0,sp,48
8: fca43c23 sd a0,-40(s0)
000000000000000c <.LBB2>:
/Users/cyao/test/main.c:5
for (int i = 0; str[i] != 0; i++)
c: fe042623 sw zero,-20(s0)
10: a01d j 36 <.L2>
0000000000000012 <.L3>:
/Users/cyao/test/main.c:6 (discriminator 3)
putchar(str[i]);
12: fec42783 lw a5,-20(s0)
16: fd843703 ld a4,-40(s0)
1a: 97ba add a5,a5,a4
1c: 0007c783 lbu a5,0(a5)
20: 2781 sext.w a5,a5
22: 853e mv a0,a5
24: 00000097 auipc ra,0x0
28: 000080e7 jalr ra # 24 <.L3+0x12>
/Users/cyao/test/main.c:5 (discriminator 3)
for (int i = 0; str[i] != 0; i++)
2c: fec42783 lw a5,-20(s0)
30: 2785 addiw a5,a5,1
32: fef42623 sw a5,-20(s0)
0000000000000036 <.L2>:
/Users/cyao/test/main.c:5 (discriminator 1)
36: fec42783 lw a5,-20(s0)
3a: fd843703 ld a4,-40(s0)
3e: 97ba add a5,a5,a4
40: 0007c783 lbu a5,0(a5)
44: f7f9 bnez a5,12 <.L3>
0000000000000046 <.LBE2>:
/Users/cyao/test/main.c:7
}
46: 0001 nop
48: 0001 nop
4a: 70a2 ld ra,40(sp)
4c: 7402 ld s0,32(sp)
4e: 6145 addi sp,sp,48
50: 8082 ret
0000000000000052 <main>:
main():
/Users/cyao/test/main.c:9
int main() {
52: 1141 addi sp,sp,-16
54: e406 sd ra,8(sp)
56: e022 sd s0,0(sp)
58: 0800 addi s0,sp,16
/Users/cyao/test/main.c:10
printf("Hello world");
5a: 000007b7 lui a5,0x0
5e: 00078513 mv a0,a5
62: 00000097 auipc ra,0x0
66: 000080e7 jalr ra # 62 <main+0x10>
/Users/cyao/test/main.c:11
example_inline("Hello! I am inlined");
6a: 000007b7 lui a5,0x0
6e: 00078513 mv a0,a5
72: 00000097 auipc ra,0x0
76: 000080e7 jalr ra # 72 <main+0x20>
/Users/cyao/test/main.c:13
return 0;
7a: 4781 li a5,0
/Users/cyao/test/main.c:14
}
7c: 853e mv a0,a5
7e: 60a2 ld ra,8(sp)
80: 6402 ld s0,0(sp)
82: 0141 addi sp,sp,16
84: 8082 ret
PS. There are colors in the dissembled code
use -Wa,-adhln as option on gcc or g++ to produce a listing output to stdout.
-Wa,... is for command line options for the assembler part (execute in gcc/g++ after C/++ compilation). It invokes as internally (as.exe in Windows).
See
>as --help
as command line to see more help for the assembler tool inside gcc

Resources