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.
Related
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.
I have the following problem: I want to use the following assembler code from my C source files using inline assembler:
.word 1
The closest I've gotten is using this inline assembler code:
asm(".word %0\n": : "i"(1));
However, this results in the following code in the generated assembler file:
.word #1
So I need a way to pass a constant that is known at compile time without adding the '#' in front of it. Is this possible using inline assembler?
Edit:
To make it more clear why I need this, this is how it will be used:
#define LABELS_PUT(b) asm(".word %0\n": : "i"((b)));
int func(void) {
LABELS_PUT(1 + 2);
return 0;
}
I can't use ".word 1" because the value will be different every time the macro LABELS_PUT is called.
Your macro has a ; at the end. So it's a whole statement, not just an expression. Don't do that.
A .word mixed in with the code of your function is usually going to be an illegal instruction, isn't it? Are you actually planning to run this binary?
You should be able to get the preprocessor to stringify your macro parameter, and let string-concatenation join it up. Then the assembler can evaluate 1+2.
#define LABELS_PUT(b) asm(".word " #b "\n")
LABELS_PUT(1+2); // becomes:
asm(".word " "1+2" "\n");
There's also https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86Operandmodifiers, some of which might work for other architectures:
asm (".word %c0" : : "i" (b))
In GCC you can do it like this:
asm __volatile__ (".word 0x1");
If you are using Visual Studio then you can try:
some_word: _asm{_emit 1000b}
It will pass into code word constant 1000b
You can get access with label some_word
I'm using GCC (correction) SDCC with the Eclipse IDE to compile C code for an 8051 architecture embedded target. I need to insert a few NOPs for timing, and I can't get the compiler to accept inline assembly code.
With __asm__ ("; This is a comment\nlabel:\n\tnop"); (as suggested below) or variations I get warning 112: function '__asm__' implicit declaration and then error 101: too many parameters, as if I'm trying to call an undeclared function. I've tried all other options in the SDCC manual section 3.14 also. __asm ... __endasm gives a syntax error on __asm, same with a single underbar, and combinations of whitespace, newlines, or the same line don't help.
If I'm piecing together the command line from the Makefile correctly (without the #include path), the CFLAGS on the SDCC command line are:
-Wp,-MD,$(#:%.rel=%.d),-MT,$#,-MP --disable-warning 110 -Wa,-p --model-medium
Moved from comment
In the sources of SDCC 3.1.0's lexer, I see that both _asm/_endasm and __asm/__endasm are supported. I haven't noticed yet support for __asm("string") in the parser yet.
Also in the lexer's code, the lexing type of the inline assembly token "blob" gets changed to CPP_ASM only if a property called preproc_asm is set to 0, as can be seen in sdcc/support/cpp/libcpp/lex.c:1900.
result->type = CPP_NAME;
{
struct normalize_state nst = INITIAL_NORMALIZE_STATE;
result->val.node.node = lex_identifier (pfile, buffer->cur - 1, false,
&nst);
warn_about_normalization (pfile, result, &nst);
}
/* SDCC _asm specific */
/* handle _asm ... _endasm ; */
if (result->val.node.node == pfile->spec_nodes.n__asm || result->val.node.node == pfile->spec_nodes.n__asm1)
{
if (CPP_OPTION (pfile, preproc_asm) == 0)
{
comment_start = buffer->cur;
result->type = CPP_ASM;
_sdcpp_skip_asm_block (pfile);
/* Save the _asm block as a token in its own right. */
_sdcpp_save_asm (pfile, result, comment_start, result->val.node.node == pfile->spec_nodes.n__asm);
}
result->flags |= ENTER_ASM;
}
else if (result->val.node.node == pfile->spec_nodes.n__endasm || result->val.node.node == pfile->spec_nodes.n__endasm1)
{
result->flags |= EXIT_ASM;
}
/* Convert named operators to their proper types. */
else if (result->val.node.node->flags & NODE_OPERATOR)
{
result->flags |= NAMED_OP;
result->type = (enum cpp_ttype) result->val.node.node->directive_index;
}
break;
The solution was to add #pragma preproc_asm - (or +) at the top of the file and to use the multiline __asm/__endasm blocks.
this link: http://www.crossware.com/smanuals/c8051/_t243.html
has this to say about inline assembly code
Assembler code can be embedded into your C source code in two ways:
using the #asm/#endasm preprocessor directives
using the _asm keyword
The pre-processor directives #asm and #endasm allow assembler code to be included anywhere within the C source code file, the only restriction being that it cannot be positioned within an expression. All lines between #asm and #endasm are passed straight through unmodified to the intermediate file processed by the assembler and so all of the rules for the cross assembler source code are supported.
The pre-processor directives #if, #ifdef, #ifndef, #else, #elif and #endif are valid between #asm and #endasm and so can be used to maintain the assembler code if required.
The _asm keyword can only be used within functions. It is used with following syntax:
_asm();
The string constant is passed straight through unmodified as a single line to the intermediate file processed by the assembler. Each should therefore be a valid line of assembler code.
One advantage of the _asm syntax is that it is subject to token replacement by the C preprocessor. Therefore the statement can be generated by a series of macros.
Also with the _asm syntax, the compiler supports a special construct to enable easy access to C variables. If the variable name is placed in the string contant within curly braces, the compiler replaces the variable name (and the curly braces) with the appropriate substring depending upon the location of the variable. See the following sections for more details.
The compiler generates upper case mnemonics and so if lower case is chosen for the in-line assembler code it can be clearly distinguished from the compiler generated code in the list file.
however, the correct format is: '_asm(" nop");' because a mnemonic assembly instruction cannot be the first thing on a line (that privilege is for labels)
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'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).