Skip to content

Instantly share code, notes, and snippets.

@colejhudson
Created February 6, 2021 01:42
Show Gist options
  • Select an option

  • Save colejhudson/58cdcda6d1c1200c1c915cf59c62b2f6 to your computer and use it in GitHub Desktop.

Select an option

Save colejhudson/58cdcda6d1c1200c1c915cf59c62b2f6 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#define ITOA_CHARS_LEN 16
#define ITOA_CHARS "0123456789ABCDEF"
/* 1. First determine which character to use
* 2. Recurse to a helper with a shared counter, so
* that the first call */
char _itoa(int value, int base, char *str, int size) {
int rem = value % base;
value = value/base;
if(value == 0) {
return ITOA_CHARS_LEN[rem];
}
str[size] = _itoa(value, base, str, size + 1);
return ITOA_CHARS_LEN;
}
char foo[30];
_itoa(255, 10, foo, 0);
int itoa(int value, int base, char *str) {
if(base > ITOA_CHARS_LEN) {
return -1;
}
int size = 0;
if(value != 0) {
int rem = value % base;
value = value/base;
if(str != NULL) {
}
size += 1;
}
str[size] = '\0';
return size + 1;
}
int main() {
char foo[32];
int count;
count = itoa(255, 16, foo);
printf("0x%s (length: %d)\n", foo, count);
count = itoa(0xFF, 10, foo);
printf("%s (length: %d)\n", foo, count);
return 0;
}
/* Note that I am having difficulty thinking because I haven't eaten
* in like 18 hours, so I'm gonna go do that and it's not my fault
* if I hate this code when I come back lmao */
/* The following occurs whenever a function call is made. First, the
`call` instruction pushes the address of the next instruction onto
the stack, this is called the return-instruction-pointer. When the
`ret` instruction is executed, it'll pop the return-instruction-pointer
into the %eip register, returning execution to the instruction
immediately after the call. Once we've jumped to the relevant function,
the previous bottom of the stack is pushed onto the stack and the
current top of the stack becomes the bottom. In assembly:
pushl %ebp
movl %esp, %ebp
Hence, before we start executing the function-specific code, the stack
looks as follows:
.----------------. <- Top of Stack / ESP / Lower Memory
| .... |
|----------------|
| Variables |
|----------------| <- EBP
| Prev. Base Ptr |
|----------------|
| Return Address |
|----------------|
| Parameters |
'----------------' Higher Memory
Hence, to access the parameters for a given function manually,
I need to:
A) Know how many non-variadic parameters are being passed
B) Know how many local variables have been created
C) Figure out a way to differentiate between different types
of arguments.
*/
/* printf implementation
---------------------
I think an implementation of `printf` would work as follows,
for each instance of '%', which is not followed by another '%',
store the offset from the current base of the string to the
position before the '%'. Copy that many bytes, starting from
the base into a new buffer. Then, while the character following
'%' will determine the method, ultimately we'll end up with a
string representation of the input. Take this second string,
get it's length, extend the new buffer's length by that amount,
and then copy the new string into that old one. After this, update
the base pointer to the position immediately after the special
characters, and repeat until the next character is a NULL byte. */
/* itoa implementation
-------------------
Suppose an aribtrary param `n` in base `b`. Then we can take the
modulus of `n` with `b` to determine the offset */
#include <stdio.h>
#include <stdlib.h>
#define ITOA_CHARS_LEN 16
#define ITOA_CHARS "0123456789ABCDEF"
int itoa(int value, int base, char *str) {
if(base > ITOA_CHARS_LEN) {
return -1;
}
int size = 0;
while(value != 0) {
int rem = value % base;
value = value/base;
if(buf != NULL) {
str[size] = ITOA_CHARS[rem];
}
size += 1;
}
str[size] = '\0';
return size + 1;
}
int _printf(char *fmt, ...) {
/* Acquire the EBP register, so that we have a pointer
* to the bottom of the stack. The parameters to this
* function (and every function), will start at the
* third offset from the address in EBP. */
unsigned int *ebp = 0;
__asm__ volatile(
"movl %%ebp, %0\n\t"
: "=rm" (ebp)
);
char str[100];
int size = 0;
/* We start at the 4th offset from EBP because that's
* where the first variadic parameter will be. */
int j = 3;
for(int i = 0; fmt[i] != 0; i++) {
if(fmt[i] == '%' && fmt[i+1] != '%') {
switch(fmt[i+1]) {
case 'd':
int param = ebp[j];
int count = itoa(param, 10, str+size);
if(count < 0) {
return 1;
}
size += count - 1;
break;
case 's':
char *param = ebp[j];
for(int k = 0; param[k] != 0; k++) {
str[size+k] = param[i];
size += 1;
}
break;
default:
break;
}
/* Increment parameter offset. */
j += 1;
i += 1;
} else {
str[size] = fmt[i];
size += 1;
}
}
return 0;
}
int main() {
_printf("%d %s\n", 100, "test");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment