In the last post we got a c program to boot, run, then segmentation fault without the C runtime. In this post The following issues are addressed
- The segmentation fault will be fixed
- Main will take arguments

Fixing the segmentation fault
This issue really is that extreme. In the previous post there was a small error. In the puts function it does not return. So the the function just ends there but continues on.
# IN: rdi points to NUL-terminated string
puts:
call strlen
mov %rdi, %rsi
mov %rax, %rdx
mov $1, %rax
mov $1, %rdi
syscall
ret
the last line is added. The puts function figuratively extends off the page. The ret instruction returns to the next instruction after the call.

The program no longer segmentation faults.
In the program what happens if arguments are passed to printf
In the last posted will only printed hello like the following
printf("hello\n");
If we update main to print a string with an argument, will it still compile. In the last post only puts need implemented to allow it to run.
printf("hello this is an arg %s\n", "this");
drum roll?
build/main.o: in function `printf':
/nix/store/81awch8mhqanda1vy0c09bflgra4cxh0-glibc-2.40-66-dev/include/bits/stdio2.h:118:(.text.startup+
0x1a): undefined reference to `__printf_chk'
Looks like printf uses printf-chk, and printf-chk needs implemented, or linked. To fix this options need to be added to the linking command. We are linking it this way for a reason. It is to avoid linking in the standard library. We link it this way intentionally. Our goal is to avoid linking in the standard library.
-lc -dynamic-linker $(LD_LIBRARY_PATH)/ld-linux-x86-64.so.2

it worked..
Adding arguments to main
Since printf works with arguments. Main should be modified to take arguments and those arguments will be passed to printf and printed as a string. This is one of the core functions of the C runtime.
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("arg %d: %s\n", i, argv[i]);
}
return 0;
}
The program compiles and runs, but prints nothing.

I will save you the pain of reading through my many attempts of getting the command line arguments to work.
I need to give this person/repo credit where credit is due. There post helped be get this working. https://github.com/lpsantil/rt0/blob/master/src/lib/00_start.c
mov %rsp, %rbp # 1
mov 0(%rsp), %rdi # 2
lea 8(%rsp), %rsi # 3
lea 8(%rsi,%rdi,8), %rdx # 4
- move stack pointer into base pointer
- move argument 1 rsp (no offset) into rdi
- load effective address from rsp offset by 8 into rsi
- load effective address rsp + rdi(argument 1) * 8 + 8 into rdx
Here is the output

One interesting note. A lot of C runtimes have 3 arguments. Argc: Count of arguments; Argv: Arguments; Envp: Environmental variables of the program. #4 is Envp. We maybe able to do something interesting with this in the next post.
Misc updates
After allowing command line arguments, I experimented with the build. Turns out the only flags we need for compiling and linking are -g (we need debugging), -nostartfile (no crt0.o, crt[etc…].o), -nofpic (no position independent code). The last argument is probably.
Makefile
CC ?= gcc
LINKER ?= gcc
CFLAGS := -g -nostartfiles -fno-pic
BUILD_DIR := build
$(BUILD_DIR)/mainc: $(BUILD_DIR)/crtSTUB.o $(BUILD_DIR)/main.o
mkdir -p $(@D)
$(LINKER) -o $@ $^ $(CFLAGS)
$(BUILD_DIR)/%.o: %.c
mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ -c $^
$(BUILD_DIR)/%.o: %.s
mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ -c $^
clean:
rm -r $(BUILD_DIR)
crtSTUB.c
linking against stdlib allows us to remove puts, the crtSTUB.c is only two functions now. one _start function, and one exit function.
.section .text
.globl _start
_start:
# assembly here
mov %rsp, %rbp # Set up the new base pointer
mov 0(%rsp), %rdi
lea 8(%rsp), %rsi
lea 8(%rsi,%rdi,8), %rdx
and $-16, %rsp
call main
mov %rax, %rdi
call exit
# IN: rdi points to return code
exit:
mov $60, %rax
syscall
main.c
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("in main: argc: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("arg: %s\n", argv[i]);
}
return 0;
}
The only change to main is adding the include for stdio.h. Since we can compile and link against stdlib. We can use printf which is inside stdio.h.
Resolution
The goal set in the last post was to find out if we need crts. From this limited experiment. It looks like we do not need a whole lot from the crts. The only part we use is setting up the stack and setting up the command line arguments. There maybe other parts we need but for basic programs in C only a slice of crt functionality is needed.
In the next post in this series I would like to try and make a malicious crtSTUB.
Leave a Reply