The "os161" operating system contains the following code. Specifically, where the syscalls are defined:
...
#include <kern/syscall.h>
...
#define SYSCALL(sym, num) \
.set noreorder ; \
.globl sym ; \
.type sym,#function ; \
.ent sym ; \
sym: ; \
j __syscall ; \
addiu v0, $0, SYS_##sym ; \
.end sym ; \
.set reorder
...
SYSCALL(fork, 0)
SYSCALL(vfork, 1)
SYSCALL(execv, 2)
SYSCALL(_exit, 3)
SYSCALL(waitpid, 4)
SYSCALL(getpid, 5)
...
At the bottom, each syscall gets a number. I can't seem to figure out what is the use of these numbers.
I'm not asking about the use of syscall numbers, I'm asking for the use of the argument num to the macro SYSCALL. I can't find where it's being used.
Even when the syscall number is moved to v0, the argument num is not used. Instead, it moves a constant defined in the file kern/syscall.h:
...
#define SYS_fork 0
#define SYS_vfork 1
#define SYS_execv 2
#define SYS__exit 3
#define SYS_waitpid 4
#define SYS_getpid 5
...
How can the argument num be useful somehow?
It is used for other tools to ease the maintenance of the source code.
The following is a quote from Understanding System Calls
syscalls.S: This file is created from syscalls-mips.S at compile time and is the actual file assembled into the C library. The actual names of the system calls are placed in this file using a script called callno-parse.sh that reads them from the kernel's header files. This avoids having to make a second list of the system calls. In a real system, typically each system call stub is placed in its own source file, to allow selectively linking them in. OS/161 puts them all together to simplify the makefiles. Adding new entries to callno.h automatically causes new user-level system call procedures to be defined when you re-build the user-level code. Each "SYSCALL(name,num)" macro statement in this file is expanded by the C pre-processor into a declaration of the appropriate system call function.
kern/syscall.h most likely is produce by one of those tools.
Related
I have a code base which uses #define in a different way then I am accustomed to.
I know that, for example, #define a 5 will replace variable a with 5 in the code.
But what would this mean:
'#define MSG_FLAG 5, REG, MSGCLR'
I tried doing it in a simple code and compiling it. It takes the last value (like the third argument as MSGCLR).
Preprocessing is largely just string replacement that happens before the "real" compilation starts. So we don't have any idea of what a variable is at this point.
The commas here are not any special syntax. This will cause any appearance of MSG_FLAG in the code to be replaced by 5, REG, MSGCLR
Most compilers have a flag that will just run the preprocessor, so you can see for yourself. On gcc, this is -E.
So to verify this, we can have some nonsense source:
#define MSG_FLAG 5, REG, MSGCLR
MSG_FLAG
Compile with gcc -E test.c
And the output is:
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
5, REG, MSGCLR
I want to mark specific C lines with sort of assembler label/symbol which will not occupy any space in the binary but by examining the linker output map file I will know all occurrences of such generated labels and, eventually, of the C code that was "marked" this way. So I want to be able to define such labels, and to make them global, and used so the linker does not throw it away
I also need some macros magic to have those labels have a unique name each time the C code is preprocessed ( to make sure each inlined instance of the function has its own label - otherwise I will have duplicate symbols, I guess )
Example :
// my build system will pass -DMYFILE_ID for each file, here I am trying to create a unique literal for each inline instance of the function
#define UN(X) #X
#define UNIQUE(X,Y) UN(X##Y)
void my_func(void)
{
_asm("GLOBAL_LABEL_"UNIQUE(MYFILE_ID,__LINE__)":\n\t")
my_c_code_I_want_to_track();
}
And what I would like to have at the end, is in the linker output symbols map file, something like that
0xsome_address GLOBAL_LABEL_12_1
0xdifferent_address GLOBAL_LABEL_12_2
0xyeanotheraddress GLOBAL_LABEL_13_1
which basically should give me an idea at which addresses my_c_code_i_want_to_track got instantiated
The whole idea is sort of inspired by how the labels in assembly are actually "symbols" that have a placement and so their addresses can be checked but they dont actually occupy its own space.
Problems :
1. Is it even possible to have assembly labels be defined like that
2. How to make those labels stay and appear in the output symbols map file
3. Something is wrong with the UNIQUE macro as I get "label redefined" when trying to compile
You can use %= (e.g. label%=:) inside an Extended-asm template to get the compiler to generate a unique number to avoid name collisions when a function containing inline-asm is inlined multiple times in one compilation unit.
#define STRINGIFY(x) #x
#define STR(x) STRINGIFY(x)
int foo(int x) {
asm("marker" __FILE__ "_line" STR(__LINE__) "_uniqueid%=:" :::);
return x+1;
}
int caller1(int x) {
return foo(x);
}
int caller2(int x) {
return foo(x);
}
compiles to the following asm with gcc -O3 (on Godbolt):
foo(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid7:
lea eax, [rdi+1]
ret
caller1(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid22:
lea eax, [rdi+1]
ret
caller2(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid41:
lea eax, [rdi+3]
ret
This of course won't assemble because / isn't a valid label character in GAS.
Using MYFILE_ID which contains only characters that can appear in symbol names, this would assemble just fine, and you should be able to see all the marker labels in nm output.
One problem is that you may get multiple copies of the same label due to inlining. Add the following attribute to functions containing these labels:
__attribute__((noinline))
Also note that you need to mark the symbol as global. Let's extract this into a macro so we can format nicely without changing the value of __LINE__:
#define MAKE_LABEL \
__asm__( \
"GLOBAL_LABEL_" UNIQUE(MYFILE_ID, __LINE__) ":" \
"\n\t.global GLOBAL_LABEL_" UNIQUE(MYFILE_ID, __LINE__) \
)
But the macro-expansion is off. Unfortunately, I cannot explain to you why this works. But here is the correct macro definition:
#define UN(X) #X
#define UNIQUE2(X,Y) UN(X##Y)
#define UNIQUE(X,Y) UNIQUE2(X,Y)
Otherwise you will get __LINE__ instead of, say, 23.
When I compile the following example code (these are essentially junk assembly statements with no real purpose) I get the following error;
def-asm-pop.c:13:3: error: expected string literal before numeric
constant
Line 13 is the uncommented "ASM" line;
#define iMOV "mov %eax,%ebx\n\t"
#define iNOP "nop\n\t"
#define iASM __asm__(iMOV iNOP)
#define MOV 0xB8
#define NOP 0x90
#define ASM __asm__(MOV NOP)
int main() {
//iASM; /* This one works when uncommented */
ASM; /* The one causes the error when uncommented */
return 0;
}
There maybe an error in my Hello World style attempt at inline assembly, but that is another stepping stone for me to overcome. At this point in time it seems I can't define a list of opcodes and then define an assembly statement list built from them, in the same way I can by defining the text commands. How can I make ASM work like the iASM statement?
As the error message states, the __asm__ operator wants a string and not a number, and in that string it wants valid assembler.
You are trying to directly write binary opcodes, this has not much to do with assembler.
This might work:
#define MOV ".byte 0xB8\n"
#define NOP ".byte 0x90\n"
The exact syntax is of course dependent on your assembler (and the appropriate machine language is dependent on your target platform). This is not much use for anything other than experimenting; it is not a good way to write code.
I have to generate redundant asm code which keeps calling different C functions
i am trying to do something like
#define CODE_GEN(func) push a \
call func
pop a
invoking something like
CODE_GEN(foo)
will generate
bash-4.1$ gcc -E mk.S
# 1 "mk.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "mk.S"
# 1 "asm_gen.h" 1
# 2 "mk.S" 2
# 13 "mk.S"
pusha call foo popa iret
but this fails in compilation
bash-4.1$ gcc -m32 mk.S
mk.S: Assembler messages:
mk.S:13: Error: junk `foo popa iret' after expression
mk.S:13: Error: suffix or operands invalid for `pusha'
is there a way to delimit asm code written in a single line in a .S file ?
semicolons ';' can be used in place of line breaks.
So something like
#define CODE_GEN(func) push a; \
call func; \
pop a;
will compile and work
Yes, by using semicolons, see the other answer. C macros expand to a single string with no line breaks, but semicolons get around that.
You can also use GNU Assembler macros. This is totally untested, could be wrong:
.macro CODEGEN func
push a
call \func
pop a
.endm
I'm working on manually constructing an IDT table in x86 assembly. I have the following macros defined using the C preprocessor in my .S file:
// sets up an entry in the idt for a trap type
#define SETUP_IDT_ENTRY( name, num, istrap, segment, dpl ) \
lea name, %edx; \
movl $(KCODE << 16), %eax; \
movw $0x8e00, %dx; \
lea (idt + (8 * num)), %edi; \
movl %eax, (%edi); \
movl %edx, 4(%edi);
// sample set of args to a call to setup_dt_entry
#define M_DIVIDE _t_divide_ep, T_DIVIDE, 0, KCODE, 0
// the call
SETUP_IDT_ENTRY( M_DIVIDE )
However, gcc complains: error: macro "SETUP_IDT_ENTRY" requires 5 arguments, but only 1 given
I thought that #define'd arguments to #define'd functions were expanded before the function call was evaluated, in which case M_DIVIDE would expand to the five arguments required and SETUP_IDT_ENTRY would be happy. I've tried various combinations of parentheses and nothing seems to be working; is there a way to make this work?
Note: I know there are alternate approaches for building IDT's in x86 assembly, but that's not the question I'm trying to answer here; I'm just trying to figure out if macros can be expanded as macro arguments.
The arguments themselves are expanded, but the number of arguments must match the macro definition. You'll need an extra macro to make it work:
#define IDT1(x) SETUP_IDT_ENTRY(x)
IDT1(M_DIVIDE)
More info here and here.
It can be done with another layer of indirection:
#define PLEASE_SETUP_IDT_ENTRY(...) SETUP_IDT_ENTRY(__VA_ARGS__)
// the delicate, civilized call
PLEASE_SETUP_IDT_ENTRY(M_DIVIDE)
(Variadic macro is not necessary if we want a new variant to accept one and only one argument. The above definition accepts one or multiple arguments).