diff options
author | FreeArtMan <dos21h@gmail.com> | 2022-08-26 10:03:34 +0100 |
---|---|---|
committer | FreeArtMan <dos21h@gmail.com> | 2022-08-26 10:03:34 +0100 |
commit | 51e6cef5d338c8ea74572f44c92dab9c0ac13dda (patch) | |
tree | 7af9b5dfca63a20b9ed5dbb009f220d31376a25b /md/notes/undefined_c/titles.md | |
parent | aff2574a12174fc58a1bd528a6a9e8c5c4b88903 (diff) | |
download | md-content-51e6cef5d338c8ea74572f44c92dab9c0ac13dda.tar.gz md-content-51e6cef5d338c8ea74572f44c92dab9c0ac13dda.zip |
Renamed undefined c
Diffstat (limited to 'md/notes/undefined_c/titles.md')
-rw-r--r-- | md/notes/undefined_c/titles.md | 1749 |
1 files changed, 0 insertions, 1749 deletions
diff --git a/md/notes/undefined_c/titles.md b/md/notes/undefined_c/titles.md deleted file mode 100644 index 731d42c..0000000 --- a/md/notes/undefined_c/titles.md +++ /dev/null @@ -1,1749 +0,0 @@ -title:Undefined C -keywords:c,linux,asm - -# Undefined C - -There is possible to run piece of code inside online c compiler like https://www.onlinegdb.com/online_c_compiler -Or run locally. With base check is done with gcc compiler. There are many small tricks around running C code -in practice that aren't covered in any generic tutorials, so here is list of topics that may arise while -coding real C code outside of tutorials. For each case there is just small example, each of those could -take whole chapter on its own. - -## Compile - - -__hello_world.c__ -```c -int main() { - printf("Hello world\n"); -} -``` - -```bash -gcc hello_world.c -o hello_world -gcc -m32 hello_world.c -o hello_world_32 #for 32bit target -``` - -## Syntax - -### Variables - -Standard list of available types - -#### Check type size - -All types have size that are declared in bytes. Some of the types are machine dependents. -like int/long, if there is needed machine independent types then there are int32_t/uint32_t/int64_t/uint64_t - -Each architecture 8bit/16bit/32bit/64bit will have different size for those types - -Use __sizeof()__ - -Running on x86 machine -```c -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -int main() { - printf("Sizeof int %lu\n",sizeof(int)); - printf("Sizeof int32_t %lu\n",sizeof(int32_t)); - printf("Sizeof int64_t %lu\n",sizeof(int64_t)); - printf("Sizeof long %lu\n",sizeof(long)); - printf("Sizeof long long %lu\n",sizeof(long long)); -} -``` - -Most safest/portable way is to use [u]int[8/16/32/64]_t types. - -Defined macros'es to get type max and min values are - -https://en.cppreference.com/w/c/types/limits - -```c -#include <limits.h> -int main() { - printf("INT_MIN %d\n",INT_MIN); - printf("INT_MAX %d\n", INT_MAX); - printf("LONG_MIN %ld\n",LONG_MIN); -} -``` - -Example from AVR __stdint.h__ -https://github.com/avrdudes/avr-libc/blob/main/include/stdint.h -Example from Libc -https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/stdint.h - - - -#### How to shoot the leg - -When code suppose to run on 32bit and 64bit platform the size of type may vary. -Need to take in account this case. - - - - - -### Functions - -Function syntax, there is nothing interesting on functions - -``` -<RETURN_TYPE> <NAME>(<TYPE> <NAME>,..) { - <EXPR> -} -``` - -Write simple function - -```c -int fun1() { - return -1; -} -``` - -Function can have multiple return statements. -Here is example whne function have 3 return values. -```c -int fun2(int i) { - if (i<0) return -1; - if (i>0) return 1; - return 0; -} -``` - -Get address of function - -```c -printf("fun1 address %016x",&fun1);//64bit platform -``` - -### If statement - -```c -if () ; -if () {} -``` - -One of the way to check error of returned functions is - -```c -if ((c = getfun()) == 0) { -} -``` - -Most simplest and outdated way to do this is when getting input from command line -```c -#include <stdio.h> -int main() { - int c; - char ch; - while ((c = getchar()) != EOF ) { - ch = c; - printf("Typed character %c\n",c); - } -} -``` - -### For cycle - -For loop is one that may involve some trickery, its -as simple as - -```c -for (<INITIAL>;<TERMINATE CONDITION>;<AFTER CYCLE>) { -} -``` - -Go over values from 1 till 10 - -```c -int i=0; -for (i=1;i<=10;i++) { - printf("%d\n",i) -} -``` - -Now lets do it from 10 till 1 - -```c -int i=0; -for (i=10;i>0;i--) { - printf("%d\n",i) -} -``` - -Now lets make one liner - -```c -for (i=0;i<10;i++,printf("%d\n",i)); -``` - -Yes there is possible to write as many expressions as needed. - - -### Structure - -Structure allows to combine types under one new type. Structure is convenient way how to combine set -of types and reuse them as one. - -```c -struct struct1 { - uint8_t a; - uint16_t b; - uint32_t c; - uint64_t d; -}; -``` - -Total intuitive size of structure would be -```c -int total_szie = sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint64_t); -int real_size = sizeof(struct1); -``` - -Types are placed inside structure to make fast access to them. Some instructions of CPU may require -to access aligned memory addresses to not have penalty on accessing types inside structure. - -To directly mess with alignment of types use attribute -```c -__attribute__ ((aligned (8))) -``` - - -Use attributes to pack structure and be not architecture dependent. - -```c -struct struct2 { - uint8_t a; - uint16_t b; - uint32_t c; - uint64_t d; -} __attribute__((packed)); -``` - -Now let check size of structure after it packed - -```c -int new_size = sizeof(struct2); -``` - -Also there is possible to add aligmnet to each time in structure -```c -struct struct3 { - uint8_t a __attribute__((aligned (8))); - uint16_t b __attribute__((aligned (8))); - uint32_t c __attribute__((aligned (8))); - uint64_t d __attribute__((aligned (8))); -} __attribute__((aligned (8))); -``` - -Now size of structure will be 32. - -All results on amd64, other arch may differ. - -### How to shoot leg -Forget that struct size is not consistent. - -### Recursion - -Recursion is technique that could be useful to write shorter code -and deal with cycles. One thing that recursion suffer is that it consumes -stack memory and its have default limit on platform. - -```c -#include <stdio.h> -#include <stdlib.h> - -int fun_r(int i) { - printf("val %d\n",i); - fun_r(i+1); - return 0; -} - -int main() -{ - fun_r(0); -} -``` - -Program will fail after its reach out of stack range. -When increase the default stack limit it go more further. - - -Check default stack size - -``` -ulimit -s -``` - -Set stack size - -``` -ulimit -s 16384 -``` - -### Macro - -There is many things useful as macros. There is many tricks in macros to emit -useful parts of code. - -Define values, as its enum. -```c -#define VAL_0 0 -#define VAL_1 1 -#define VAL_LAST VAL_1 -``` - -Multiline macro -```c -#define INC_FUN(TYPE) TYPE inc_##TYPE(a TYPE){\ - TYPE c=1\ - return a + c\ -} - -INC_FUN(int) -INC_FUN(char) -INC_FUN(double) -INC_FUN(notype) -``` - -to check code expansion of macro run - -``` -gcc -E <SOURCE_FILE> -``` - - - -http://main.lv/writeup/c_macro_tricks.md - - -https://jadlevesque.github.io/PPMP-Iceberg/ - - -### Pointers - -One the C most loved feature is pointers, they allow to access addresses without any sanity check -and they dont have any lifetime, so anything is possible with those. - -Pointer contains address which is interpreted according of pointer type - -```c -int c; -int ptr=&c; -``` - -Go over array of chars -```c -#include <stdio.h> -#include <stdlib.h> - -int main() { - char s[]="asd"; - char *c=&s; - while (*c != 0) { - printf("NExt char %c addr %016x\n",*c,c); - c++; - } -} -``` -Go over array of ints -```c - int i=0; - int arr[] = {9,7,5,3,1}; - int *ptr = arr; - while (i<5) { - printf("Number value %d addr %016x\n",*ptr, ptr); - ptr++; - i++; - } -``` - -Pointer arithmetics like +1 will move to next address that is offset of type size. -As example below structure size is 12, and increment of pointer to that structure -increment address to sizeof structure. And yes address is pointing to not mapped memory, so it -will segfault if accessed. - -```c -struct size12 { - int a,b,c; -} - -int main() { - struct size12 *s=0; - s++; - printf("%016x\n",s); - s++; - printf("%016x\n",s); -} -``` - -Double pointers are pointers to pointers - -```c -#include <stdio.h> - -int main(int argc, char **argv) { - char *arg = argv[0]; - printf("Program name %s\n",arg); -} -``` - -#### How to shoot the leg -Run pointer in while loop incrementing pointer. It will stop only when segfaults. - -Dont initialize pointer and it will have random value. - - - -### Allocate memory - -From programs perspective memory allocation is adding address range to executable that can be addressed. - -malloc should be accompanied with free statement, otherwise it will have memory leaks. - -```c -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main() { - char *c = malloc(16); - memset(c,0,16); - int *arr = malloc(16*sizeof(int)); - memset(arr,0,16*sizeof(int)); - free(c); - free(arr); -} -``` - -### Signed/Unsigned - -Signed and unsigned variables differ just in one bit interpretation. But they have different behavior on minimal and maximal values. - - -```c -#include <stdio.h> -#include <limits.h> -int main() -{ - int i=INT_MAX; - unsigned int u=UINT_MAX; - - printf("i=%d\n",i); - printf("u=%u\n",u); - - i++; - u++; - printf("i=%d\n",i); - printf("u=%u\n",u); - i=0; - u=0; - i--; - u--; - printf("i=%d\n",i); - printf("u=%u\n",u); - -} -``` - -### Endianess - - -```c -#include <stdlib.h> -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdint.h> - -int main() { - int arr[4] = {0x00112233,0x44556677,0x8899AABB, 0xCCDDEEFF}; - printf("%08x\n",arr[0]); - printf("%08x\n",arr[1]); - printf("%08x\n",arr[2]); - printf("%08x\n",arr[3]); - - FILE *f = fopen("int.hex","w+"); - fprintf(f,"%08x",arr[0]); - fprintf(f,"%08x",arr[1]); - fprintf(f,"%08x",arr[2]); - fprintf(f,"%08x",arr[3]); - fclose(f); - - int fd=open("int.bin",O_CREAT|O_RDWR,S_IWUSR|S_IRUSR|S_IRGRP|S_IRWXO); - write(fd,arr,sizeof(arr)); - close(fd); - - int i; - fd = open("int.bin2",O_CREAT|O_RDWR,S_IWUSR|S_IRUSR|S_IRGRP|S_IRWXO); - for (i=0;i<4;i++) { - uint32_t val = (arr[i]>>16) &0x0000ffff; - val += (arr[i]<<16)&0xffff0000; - write(fd,&val,sizeof(uint32_t)); - } - close(fd); -} -``` - -While saving formated values to file you will get what you expect -``` -$ cat int.hex -00112233445566778899aabbccddeeff -``` - -Saving just memory dump of all values, will give you different result -``` -$ hexdump int.bin -0000000 2233 0011 6677 4455 aabb 8899 eeff ccdd -0000010 -``` - -Need to swap 16bit pairs to look same as value memory dump -``` -$ hexdump int.bin2 -0000000 0011 2233 4455 6677 8899 aabb ccdd eeff -0000010 -``` - -### Compiler flags - -Compiler have whole list of command line arguments that you can enable for different purposes, lets look into some of them -https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html - -Lets try to apply some of the flags to examples above. - -Best starte options is, those will give you more warnings. - -``` --Wall -Wextra -``` - -Most of the examples here was written in sloppy style, so adding extra checks like will find more issues with code, probably -all of provided examples will show issues with this extra compiler flags - -``` -Wformat-security -Wduplicated-cond -Wfloat-equal -Wshadow -Wconversion -Wjump-misses-init -Wlogical-not-parentheses -Wnull-dereference -``` - -To get all macroses expanded in C code add compiler flag. Output will be C source with all macro expansion -``` --E -``` - -Output resulting file not to binary but to generated assembly add -``` --S -``` - -More readable output can be obtained with - -``` -gcc FILE.c -Wa,-adhln=FILE.S -g -fverbose-asm -masm=intel -``` - -Basic compiler optimisation flags that can speedup program or make it smaller - -``` --O -O0 -O1 -O2 -O3 -Os -Ofast -Og -Oz -``` - -https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options - -https://panthema.net/2013/0124-GCC-Output-Assembler-Code/ -https://blogs.oracle.com/linux/post/making-code-more-secure-with-gcc-part-1 - - -### Shared library - -Shared library is common way how to reuse big chunks of code. - -```c -#include <stdio.h> -int fun1() { - return 1; -} - -int fun2() { - printf("Function name fun2\n"); -} - -int fun3(int a, int b) { - return a+b; -} -``` - -``` -$ gcc -c lib_share.c -$ gcc -shared -o lib_share.so libshare.o -$ ldd lib_share.so - linux-vdso.so.1 (0x00007ffdb994d000) - libc.so.6 => /usr/lib/libc.so.6 (0x00007f0c39400000) - /usr/lib64/ld-linux-x86-64.so.2 (0x00007f0c39835000) -``` - -Now lets link to our binary -```c -#include <stdio.h> - -//functions that are implemented in shared lib -int fun1(); -int fun2(); -int fun3(int a, int b); - -int main() { - fun1(); - fun2(); - fun3(); -} -``` - -``` -$ gcc -L. -lshare use_share.c -o use_share -./use_share -./use_share: error while loading shared libraries: libshare.so: cannot open shared object file: No such file or directory -ldd ./use_share - linux-vdso.so.1 (0x00007ffedcad5000) - libshare.so => not found - libc.so.6 => /usr/lib/libc.so.6 (0x00007f7b99a00000) - /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f7b99c90000) -``` - -Library is not in search path -``` -$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd` -$ ./use_share -$ ldd use_share - linux-vdso.so.1 (0x00007fffc415c000) - libshare.so => /your/path/libshare.so (0x00007f48b03c6000) - libc.so.6 => /usr/lib/libc.so.6 (0x00007f48b0000000) - /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f48b03d2000) -``` - -Other way is to set custom library search location. Lets set it to search in current directory. -And no need to modify LD_LIBRARY_PATH - -``` -$ gcc use_share.c -o use_share -L. -lshare -Wl,-rpath=./ -$ ldd ./use_share - linux-vdso.so.1 (0x00007fff5c964000) - libshare.so => ./libshare.so (0x00007f791000f000) - libc.so.6 => /usr/lib/libc.so.6 (0x00007f790fc00000) - /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f791001b000) -``` - -So now executable runs libshare from local directory. Ofc there is possible to install shared library into systems /usr/lib - -### Static library - - - - -### Static binary - -Static binary don't use any shared libraries, and its possible to built it once and distribute on other platforms -without need to install dependencies. - - -```c -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char **argv) { - return 0; -} -``` - -First step to compile file and see that is dynamically lined -``` -$ gcc static_elf.c -o static_elf -$ file static_elf -static_elf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bc6ac706075874858e1c4a8accf77e704f4ea25a, for GNU/Linux 4.4.0, with debug_info, not stripped -$ ldd ./static_elf - linux-vdso.so.1 (0x00007ffccef49000) - libc.so.6 => /usr/lib/libc.so.6 (0x00007fcbb8800000) - /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fcbb8b63000) - -``` - -After adding static option we can verify that tools now report it as statically linked. Size of binary increased as all functions -that require to run executable are now contained in binary. - -``` -$ gcc static_elf.c -static -o static_elf -$ file static_elf -static_elf: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=c54d2e4d2a3d11fe920bee9a44af045c6f67ab56, for GNU/Linux 4.4.0, with debug_info, not stripped -$ ldd static_elf - not a dynamic executable -``` - -Statically compiled file should work on most platforms. - - - -### Atomic -HERE - -### Multithreading -HERE - - -<!-- -### stdin,stdout,stderr -### Styles - ----> - - - - -## Basic usage - -### File manipulation with libc - -Create file open data using libc functions - -```c -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main() { - FILE *f = fopen("file.txt","w+"); - char *s = "Hello"; - fwrite(s,1,strlen(s),f); - fclose(f); -} -``` - -Open file and read data back - -```c -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main() { - FILE *f = fopen("file.txt","r"); - char buf[128]; - int r; - r = fread(buf,1,128,f); - buf[r] = 0; - printf("->%s\n",buf,r); - fclose(f); -} -``` - -### File manipulation with syscalls - -Now lets do the same without using libc functions using syscall function to directly use syscalls, -its also straightforward to rewrite example for assembly. - -```c -#include <unistd.h> -#include <fcntl.h> -#include <sys/syscall.h> -#include <string.h> - -int main(void) { - int fd = syscall(SYS_open, "sys.txt", O_CREAT|O_WRONLY, S_IRWXU|S_IRGRP|S_IXGRP); - char s[] = "hello sycall\n"; - syscall(SYS_write, fd, s, strlen(s)); - syscall(SYS_close, fd); - return 0; -} -``` - - -Read data from file - -```c -#include <unistd.h> -#include <fcntl.h> -#include <sys/syscall.h> -#include <string.h> - -int main(void) { - int fd = syscall(SYS_open, "sys.txt", O_RDONLY); - char s[128]; - int r = syscall(SYS_read, fd, s, 128); - s[r] = 0; - syscall(SYS_close, fd); - syscall(SYS_write, 0, s, r); - return 0; -} -``` - -## Advanced topics - -### Kernel module - -Linux kernel, macos kernel and *BSD's kernels written in C, -so there is possibility to write kernel modules in C for some of those. - -Example will not match some specific things to local distribution. - -```c - -``` - -http://main.lv/writeup/kernel_hello_world.md - -### Linking - -Linking is one of the most interesting parts of compiling of C code. When object file is created -it contains functions and variables that can be of different type. And linking tries to resolve -all of those. So there is possible to have fun with linking and content of object files. - - -First example is piece of C code that can be compiled to object file, but it will not able to -resolve to executable. -``` -gcc -c link_elf.c -``` -```c -int main() { - fun1(); - fun2(); -} -``` -So we can see that fun1 and fun2 are marked as undefined in object file. If we try compile it will not able to find those. -So lets create one more object file -``` -$ readelf -a link_elf.o - -Symbol table '.symtab' contains 6 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS link_elf.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 31 FUNC GLOBAL DEFAULT 1 main - 4: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND fun1 - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND fun2 - -``` -__link_fun1.c__ -```c -void fun1() { - printf("Hello fun1\n"); -} -void fun2() { - printf("Hello fun2\n"); -} -``` - -So now we have object file with funtions that are defined. and we see that its now have undefine pritnf/puts function there. - -``` -readelf -a link_fun1.o -Symbol table '.symtab' contains 7 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS link_fun1.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 0000000000000000 22 FUNC GLOBAL DEFAULT 1 fun1 - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts - 6: 0000000000000016 22 FUNC GLOBAL DEFAULT 1 fun2 - -``` - -we can merge both of those files together -```shell -gcc -o link_elf link_elf.o link_fun1.o -``` -The function in object files dont have any idea about input output types. That why anything can be linked that just match name -lets rewrite code like this - -```c -int fun1(int i) { - printf("Hello fun1\n"); -} -int fun2(int i) { - printf("Hello fun2\n"); -} -``` -And this links without issue. Theat this as 2 sets that are merge together only few thins know when linking things. -Return type, and function arguments arent exposed when object file is created. - -Functions can have aliases. - -__link_fun2.c__ - -```c -static void fun2() { - printf("hello 2\n"); -} __attribute__ ((alias("fun1"))); -``` - -Now function is local. - -``` -Symbol table '.symtab' contains 6 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS link_fun2.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 0000000000000000 22 FUNC LOCAL DEFAULT 1 fun2 - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts -``` - -Lets compile all object to executable. And the function fun2 isnt used in this case, - - - -``` -$ gcc link_fun1.o link_fun2.o link_elf.o -o link_elf -$ ./link_elf -Hello fun1 -Hello fun2 - -``` - - - -lets witch aliasing between 2 functions **fun2** - - -``` -link_fun1.o - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS link_fun1.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 000000000000001d 29 FUNC LOCAL DEFAULT 1 fun2 - 5: 0000000000000000 29 FUNC GLOBAL DEFAULT 1 fun1 - 6: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts - -link_fun2.o - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS link_fun2.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 0000000000000000 22 FUNC GLOBAL DEFAULT 1 fun2 - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts - -``` - -``` -$ gcc link_fun1.o link_fun2.o link_elf.o -o link_elf -$ ./link_elf -Hello fun1 -hello 2 -``` - -So all of this plays role in linking object files. -There is more interesting utilit called ld its doing things on lower level then gcc. - - -### Extern - -### Attributes -PASS -### Creating shared library -PASS -### Create static libraries -PASS -### Join all objects together -PASS -### Compile with musl - -The libc is not the only option as standard c library, there is few others one of them is musl - -``` -$ musl-gcc hello_world.c -o hello_world -$ file ./hello_world -hello_world_musl: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, not stripped -``` - - -### Inspect elf files - -There is few utilities that help to check if elf file is ok. - -ldd show what kind of shared libraries elf will try to load - -``` -$ ldd hello_world - linux-vdso.so.1 (0x00007fffcb2ae000) - libc.so.6 => /usr/lib/libc.so.6 (0x00007ffb80c00000) - /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffb80fb9000) - -``` - -Readelf allows to inspect content of elf files, headers and interpret values in headers. -In few example above we allready used that feature to check content of compiled objectfiles. - -``` -$ readelf -s ./hello_world -Symbol table '.symtab' contains 37 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS abi-note.c - 2: 000000000000039c 32 OBJECT LOCAL DEFAULT 4 __abi_tag - 3: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c - 4: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c - 5: 0000000000001070 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones - 6: 00000000000010a0 0 FUNC LOCAL DEFAULT 14 register_tm_clones - 7: 00000000000010e0 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux - 8: 0000000000004030 1 OBJECT LOCAL DEFAULT 25 completed.0 - 9: 0000000000003df0 0 OBJECT LOCAL DEFAULT 20 __do_global_dtor[...] - 10: 0000000000001130 0 FUNC LOCAL DEFAULT 14 frame_dummy - 11: 0000000000003de8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_in[...] - 12: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello_world.c - 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c - 14: 00000000000020b0 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__ - 15: 0000000000000000 0 FILE LOCAL DEFAULT ABS - 16: 0000000000003df8 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC - 17: 0000000000002010 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR - 18: 0000000000004000 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_ - 19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...] - 20: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...] - 21: 0000000000004020 0 NOTYPE WEAK DEFAULT 24 data_start - 22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 - 23: 0000000000004030 0 NOTYPE GLOBAL DEFAULT 24 _edata - 24: 0000000000001154 0 FUNC GLOBAL HIDDEN 15 _fini - 25: 0000000000004020 0 NOTYPE GLOBAL DEFAULT 24 __data_start - 26: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ - 27: 0000000000004028 0 OBJECT GLOBAL HIDDEN 24 __dso_handle - 28: 0000000000002000 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used - 29: 0000000000004038 0 NOTYPE GLOBAL DEFAULT 25 _end - 30: 0000000000001040 38 FUNC GLOBAL DEFAULT 14 _start - 31: 0000000000004030 0 NOTYPE GLOBAL DEFAULT 25 __bss_start - 32: 0000000000001139 26 FUNC GLOBAL DEFAULT 14 main - 33: 0000000000004030 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__ - 34: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...] - 35: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@G[...] - 36: 0000000000001000 0 FUNC GLOBAL HIDDEN 12 _init -``` - -### No standard library - -Lets write hello world without libc. - -__noc.c__ -```c -void _start() { - -} -``` - -``` -$ gcc -c noc.c -$ ld -dynamic-linker /lib/ld-linux.so.2 noc.o -o noc -$ file noc -noc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped -``` - -Next step to make it more working then segfaulting. - -```c -void _start() { - asm ( \ - "movl $1,%eax\n" \ - "xor %ebx,%ebx\n" \ - "int $128\n" \ - ); -} -``` - -Now this is all about calling the syscalls - -Lets print the message -```c -signed int write(int fd, const void *buf, unsigned int size) -{ - signed int ret; - asm volatile - ( - "syscall" - : "=a" (ret) - // EDI RSI RDX - : "0"(1), "D"(fd), "S"(buf), "d"(size) - : "rcx", "r11", "memory" - ); - return ret; -} - -void _start() { - write(1,"no libc",8); - asm ( \ - "movl $1,%eax\n" \ - "xor %ebx,%ebx\n" \ - "int $128\n" \ - ); -} -``` - -http://main.lv/writeup/making_c_executables_smaller.md - -### Memory leaks - -Memory leaks is cruitial part of C language. Default case when they are detected are -when allocated memory wasn free'd after use. If amount of this type of memory increasing then -its can eventually fill whole memory and system will be unresponsive. Here is simple example -how memory leak created and how to detect it. - -```c -#include <stdlib.h> - -int main() { - - char *ptr = malloc(12); - - return 0; -} -``` - -The best way to detect it to use valgrind. - -``` -$ valgrind ./malloc - -==778== HEAP SUMMARY: -==778== in use at exit: 12 bytes in 1 blocks -==778== total heap usage: 2 allocs, 1 frees, 1,036 bytes allocated -``` - -There is seen 2 allocs and 1 free. But we see that 12bytes after exit. So our created leak is detected. -More complex example. So now we created leaking function and we called it 5 times. But in larger code -base it would be nice to see location of leaks. - -```c -#include <stdlib.h> - -int* mem_alloc(int sz) { - int *ret=NULL; - - if (sz < 0) { - return NULL; - } - - ret = malloc(sz*sizeof(int)); - - if (sz>10) { - return NULL; - } - - return ret; - -} - -int main() { - - mem_alloc(0); - - free(mem_alloc(1)); - - mem_alloc(100); - - free(mem_alloc(2)); - - mem_alloc(10); - - return 0; -} -``` - -There is 3 blocks that leaks, and we see where its comming from there is possible to guess but it would better -to have position of where leak located. - -``` -valgrind --leak-check=full --track-origins=yes --log-file=log.txt ./memleak2 - -==4974== HEAP SUMMARY: -==4974== in use at exit: 440 bytes in 3 blocks -==4974== total heap usage: 5 allocs, 2 frees, 452 bytes allocated -==4974== -==4974== 0 bytes in 1 blocks are definitely lost in loss record 1 of 3 -==4974== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==4974== by 0x109179: mem_alloc (in /home/fam/prog/c/undefined_c/memleak2) -==4974== by 0x10919E: main (in /home/fam/prog/c/undefined_c/memleak2) -==4974== -==4974== 40 bytes in 1 blocks are definitely lost in loss record 2 of 3 -==4974== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==4974== by 0x109179: mem_alloc (in /home/fam/prog/c/undefined_c/memleak2) -==4974== by 0x1091D6: main (in /home/fam/prog/c/undefined_c/memleak2) -==4974== -==4974== 400 bytes in 1 blocks are definitely lost in loss record 3 of 3 -==4974== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==4974== by 0x109179: mem_alloc (in /home/fam/prog/c/undefined_c/memleak2) -==4974== by 0x1091BA: main (in /home/fam/prog/c/undefined_c/memleak2) -==4974== -==4974== LEAK SUMMARY: -==4974== definitely lost: 440 bytes in 3 blocks -==4974== indirectly lost: 0 bytes in 0 blocks -==4974== possibly lost: 0 bytes in 0 blocks -==4974== still reachable: 0 bytes in 0 blocks -==4974== suppressed: 0 bytes in 0 blocks -``` - -Add compilation option __g3__ - -``` -gcc -g3 memleak2.c -o memleak2 -``` - -Now it shows source lines and trace from where the leaking code where called. Thats looks better now. - -``` -valgrind --leak-check=full --track-origins=yes --log-file=log.txt ./memleak2 - -==5073== HEAP SUMMARY: -==5073== in use at exit: 440 bytes in 3 blocks -==5073== total heap usage: 5 allocs, 2 frees, 452 bytes allocated -==5073== -==5073== 0 bytes in 1 blocks are definitely lost in loss record 1 of 3 -==5073== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==5073== by 0x109179: mem_alloc (memleak2.c:10) -==5073== by 0x10919E: main (memleak2.c:22) -==5073== -==5073== 40 bytes in 1 blocks are definitely lost in loss record 2 of 3 -==5073== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==5073== by 0x109179: mem_alloc (memleak2.c:10) -==5073== by 0x1091D6: main (memleak2.c:30) -==5073== -==5073== 400 bytes in 1 blocks are definitely lost in loss record 3 of 3 -==5073== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) -==5073== by 0x109179: mem_alloc (memleak2.c:10) -==5073== by 0x1091BA: main (memleak2.c:26) -==5073== -==5073== LEAK SUMMARY: -==5073== definitely lost: 440 bytes in 3 blocks -==5073== indirectly lost: 0 bytes in 0 blocks -==5073== possibly lost: 0 bytes in 0 blocks -==5073== still reachable: 0 bytes in 0 blocks -==5073== suppressed: 0 bytes in 0 blocks -==5073== -``` - - -### Code coverage - -Compile file with extra flags and generate gcov file output. -Ther is only one branch not used. Coverage should show with part isnt used. - -```c -#include <stdio.h> - -int fun1(int a) { - if (a < 0) { - printf("Smaller then zero\n"); - } - if (a==0) { - printf("Equails to zero\n"); - } - if (a>0) { - printf("Bigger then zero\n"); - } -} - -int main() { - - printf("Start\n"); - fun1(0); - fun1(1); - - return 0; -} -``` - -``` -$ gcc -fprofile-arcs -ftest-coverage coverage.c -o coverage -$ gcov ./coverage -File 'coverage.c' -Lines executed:92.31% of 13 -Creating 'coverage.c.gcov' - -Lines executed:92.31% of 13 - -``` - -Gcov file content. So we scant see with line wasnt executed. - -```c - -: 0:Source:coverage.c - -: 0:Graph:coverage.gcno - -: 0:Data:coverage.gcda - -: 0:Runs:1 - -: 1:#include <stdio.h> - -: 2: - 2: 3:int fun1(int a) { - 2: 4: if (a < 0) { - #####: 5: printf("Smaller then zero\n"); - -: 6: } - 2: 7: if (a==0) { - 1: 8: printf("Equails to zero\n"); - -: 9: } - 2: 10: if (a>0) { - 1: 11: printf("Bigger then zero\n"); - -: 12: } - 2: 13:} - -: 14: - 1: 15:int main() { - -: 16: - 1: 17: printf("Start\n"); - 1: 18: fun1(0); - 1: 19: fun1(1); - -: 20: - 1: 21: return 0; - -: 22:} -``` - -### Profiling - -Some parts of code can take substantial amount of time and those parts need to be identified. - -```c -#include <stdio.h> -#include <stdlib.h> -#include <math.h> - -void slow_sin() { - float r=0.0f; - for (int i=0;i<10000000;i++) { - r += sinf(M_PI/8); - } -} - -void slower_sin() { - double r=0.0f; - for (int i=0;i<10000000;i++) { - r += sin(M_PI/8); - } -} - -void fast_sin() { - float pre_calc = sinf(M_PI/8); - float r = 0.0f; - for (int i=0;i<10000000;i++) { - r += pre_calc; - } -} - -int main() { - slow_sin(); - slower_sin(); - fast_sin(); -} -``` - -Compile and rung with profiling - -``` -gcc -pg perf_speed.c -o perf_speed -lm -./perf_speed -gprof perf_speed gmon.cov -``` - -### Sanitizer - -C as a greate language have good features in standart such as undefined behaviour. And -also there is possible to overwrite any data you whant with your code. One of the favorite -mistake is to write some buffer overruns. Its possible to catch this type of errors with -stack protection - -So in code belove there is possible to write in to array of size 8 more then 8 characters. This is because the is no any boundry check. -C runtime will be able to detect this kind of things. - - -```c -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -void fun(char *str,int size) { - char local_var[8]; - memcpy(local_var, str, size); - printf("Whats inside a stack? %s\n",local_var); -} - -int main() { - char some_str1[] = "Hello!"; - char some_str2[] = "Hello all!!!"; - - fun(some_str1,strlen(some_str1)); - fun(some_str2,strlen(some_str2)); -} -``` - -``` -Whats inside a stack? Hello! -Whats inside a stack? Hello all!!! -*** stack smashing detected ***: terminated -fish: Job 1, './stack_overrun' terminated by signal SIGABRT (Abort) -``` - -If this isnt happening there is possible to add __-fstack-protector__ to compile flags. - -C have whole list of undefined behaviours incorporated in standard -https://en.cppreference.com/w/c/language/behavior - - - -functions __f__ variable __a__ isnt initialized so its undefined behaviour but there still will be some value. Run few -times and each time it returns new value when __f(0)__. -```c -#include <stdio.h> - -size_t f(int x) -{ - size_t a; - if(x) // either x nonzero or UB - a = 42; - return a; -} - -int main() { - printf("%d\n",f(0)); - printf("%d\n",f(1)); - printf("%d\n",f(42)); -} -``` - -Division by zero. Function __f__ dont check if divisor is 0. Programm going to abort. -add flag __-fsanitize=integer-divide-by-zero__ and it will be detected at runtime - -```c -#include <stdio.h> - -size_t f(int x) -{ - return 10/x; -} - -int main() { - printf("%d\n",f(0)); - printf("%d\n",f(1)); - printf("%d\n",f(42)); -} -``` - -``` -undefined_b.c:5:14: runtime error: division by zero -fish: Job 1, './undefined_b' terminated by signal SIGFPE (Floating point exception) -``` - -<!-- -### FARMA-C ----> - - - - -### Write plugins -### Preload library - - -## Embedding C - -Most of the programming languages support embeding C. As C language have where simple -functiong naming when its mangled to object format it makes it easy target when -linking with other languages. Most of other languages have incompatible naming for -functions when compiled to binary. - -### Embed in C++ - -__lib.h__ -```c -#include <stdlib.h> -#include <stdio.h> - -int fun_secret_1(); -``` - -__lib.c__ -```c -#include "lib.h" - -int fun_secret_1() { - printf("Hello from C\n"); - return -1; -} -``` - -First thing to notice is when file is compiled with C++ is that the name of the function are in different format -then when its compiled with C. -``` -$ g++ -c lib.c -$ readelf -s lib.o -Symbol table '.symtab' contains 6 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS lib.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z12fun_secret_1v - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts - -``` - -Lets tell C++ that file is C language by adding **extern "c"** - -__lib.h__ -```c -#include <stdlib.h> -#include <stdio.h> - -extern "C" { - -int fun_secret_1(); - -} -``` - -__lib.c__ -```c -#include "lib.h" - -extern "C" { - -int fun_secret_1() { - printf("Hello from C\n"); - return -1; -} - -} -``` - -Now compiled object file have C function names. - -``` -$ g++ lib.c -c -$ readelf -s lib.o - -Symbol table '.symtab' contains 6 entries: - Num: Value Size Type Bind Vis Ndx Name - 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND - 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS lib.c - 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text - 3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata - 4: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 fun_secret_1 - 5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts - -``` - -__cppembed.cpp___ -```cpp -#include "lib.h" - -int main() { - fun_secret_1(); -} -``` - -Doing oposite way running C++ from C -[/writeup/wraping_c_plus_plus_exceptions_templates_and_classes_in_c.md](/writeup/wraping_c_plus_plus_exceptions_templates_and_classes_in_c.md) - -### Embed in Go - -__lib.h__ -```c -#include <stdlib.h> -#include <stdio.h> - -int fun_secret_1(); -``` -__lib.c__ -```c -#include "lib.h" - -int fun_secret_1() { - printf("Hello from C\n"); - return -1; -} -``` - -__main.go__ -```go -package main -// #cgo CFLAGS: -g -Wall -// #include <stdlib.h> -// #include "lib.h" -import "C" -import ( - "fmt" - -) -func main() { - fmt.Println("Start program") - C.fun_secret_1() - fmt.Println("End program") -} -``` - -``` -go build -``` - -[https://karthikkaranth.me/blog/calling-c-code-from-go/](https://karthikkaranth.me/blog/calling-c-code-from-go/) - - -### Embed in Swift - -[/writeup/linux_hello_world_in_swift.md](/writeup/linux_hello_world_in_swift.md) - -### Embed in Rust - -__lib.c__ -```c -#include <stdio.h> -#include <stdlib.h> - -int fun_secret_1() { - printf("Hello from C\n"); - return -1; -} -``` - -```rust -extern "C" { - fn fun_secret_1(); -} - -//rustc main.rs -o hello -fn main() { - println!("Start program"); - unsafe {fun_secret_1()} - println!("End program"); -} -``` - -Compile with - -``` -gcc -c lib.c -gcc -shared lib.o -o liblib.so -rustc main.rs -l lib -L . -o hello -C link-arg="-Wl,-rpath=./" -``` - -[https://dev.to/xphoniex/how-to-call-c-code-from-rust-56do](https://dev.to/xphoniex/how-to-call-c-code-from-rust-56do) - -### Lua in C - -[/writeup/embedding_lua_in_c.md](/writeup/embedding_lua_in_c.md) - -### Python in C - - - -## Multiplatform - -### Different flags - -### Check architecture - - - -```c -``` - -### AArch64 - -https://snapshots.linaro.org/gnu-toolchain/13.0-2022.08-1/aarch64-linux-gnu/ - -download any of the version of gcc and extract - -Add bin directory location to env variable PATH - -``` -export PATH=$PATH:`pwd` -``` - -___main.c__ -```c -#include <stdio.h> - -int main() { - printf("Hello world arm64\n"); -} -``` - -``` -$ arch64-linux-gnu-gcc main.c -o main -$ ./main -qemu-aarch64: Could not open '/lib/ld-linux-aarch64.so.1': No such file or directory -$ file ./main -./main: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=12448d90030e2ad23dbe6b7bc82a4fa7b7de9659, for GNU/Linux 3.7.0, with debug_info, not stripped -``` - -Download sysroot image from linaro page. -With running - -``` -strace ./main -``` - -It showed that searched path for libraries are in - -``` -/usr/gnemul/qemu-aarch64/lib/ -``` - -Found missing libc and ld-linux-aarch64 inside sysroot archive and copied at searched location amd now AArch64 binary is running. - -``` -$ ./main -Hello world arm64 -``` - -### AVR8 - -AVR is 8bit CPU that is quite popular for hobbiest. As baremetal device its doesnt have full libc support, -and needs some setup before its possible to do basics things with it. - -__avr_echo.c__ -```c -#include <avr/io.h> - -#define FOSC 16000000UL -#define BAUD 9600 -#define MYUBRR FOSC/16/BAUD-1 - -void USART_Init( unsigned int ubrr) -{ - UBRRH = (unsigned char)(ubrr>>8); - UBRRL = (unsigned char)ubrr; - UCSRB = (1<<RXEN)|(1<<TXEN); - UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0); -} - -int main() -{ - char c; - USART_Init( MYUBRR ); - while(1) - { - while ( !(UCSRA & (1<<RXC))){}; - c = UDR; - while (!(UCSRA & (1<<UDRE))){}; - UDR = c; - } - return 0; -} -``` - -``` -avr-gcc avr_echo.c -mmcu=atmega16 -Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -o avr_echo.out -``` - -Next steps woule be to programm it, in case you have ISPv2 programmer and ATmega16 chip - -```bash -avr-objdump -s --disassemble avr_echo.out > avr_echo.s -avr-objcopy -j .text -O ihex avr_echo.out avr_echo.hex -avrdude -pm16 -cavrispv2 -Pusb -U flash:w:avr_echo.hex -``` - -### Emscripten - -[/writeup/web_assembly_sdl_example.md](/writeup/web_assembly_sdl_example.md) - - - - - - - |