One or two weird linking errors with some inline assembly - c

I have the following short application (The original is taken from: https://www.aldeid.com/wiki/X86-assembly/Instructions/str) I just modified it (tried to...) to make it compilable on linux too.
#include <stdio.h>
unsigned char mema[4] = {0, 0, 0, 0};
void test4 (void)
{
asm (
"str mema\n"
);
printf ("\n[+] Test 4: STR\n");
printf ("STR base: 0x%02x%02x%02x%02x\n", mema[0], mema[1], mema[2], mema[3]);
if ((mema[0] == 0x00) && (mema[1] == 0x40))
printf ("Result : VMware detected\n\n");
else
printf ("Result : Native OS\n\n");
}
int main () {
test4();
}
and when I try to compile it:
$ gcc -ggdb -O0 -fPIC -x c ./pill.c -o pill
it gives me the wird error:
/usr/bin/ld: /tmp/ccDhGo05.o: relocation R_X86_64_32S against symbol `mema' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
However the same thing works on an online compiler: https://gcc.godbolt.org/z/RYWjy8
... except if I move the variable mema to be a local variable in the function test4 when it complains both places about
undefined symbol `mema'
And another weirdity: same stuff works when compiled with clang.
Any idea why this happens? (Not considering the required privilege level for STR, and other thingies, I am just interested in the linking error and why does it work on one system / compiler and not on another.

When you write asm("str mema\n"); the compiler literally writes str mema into the assembly file.
In a program compiled with -fPIC the assembly code is not allowed to contain any addresses of anything in the program (that's what position-independent means). So you aren't allowed to have an instruction that contains the address of a global variable mema.
Local variables aren't things that the assembler knows about to begin with.
If you want an assembly instruction to read or write a variable, you need to tell the compiler what you want to read or write, and let the compiler figure out what exact code to write:
asm("str %0" : "=m" (mema));
"=m" says that the value is an output (=) and it has to be a memory location (m) but the compiler gets to figure out what the actual location should be.
The GCC manual has more information (clang works the same way).

Related

Undefined symbol error when trying to mov into variable which defined in C code above assembly

I am trying to run assembly code inside C code(on CLion). I defined variable x outside of assembly insert and tried to mov a number into it but compiler says x is undefined. I don't get how to make it see variables. Also I have to use intel syntax.
int main(int argc, char** argv) {
short int x = 0;
__asm__ (
".intel_syntax noprefix\n\t"
"mov eax, 0x02\n\t"
"mov x, eax\n\t"
".att_syntax prefix\n\t"
);
printf("%d", x);
}
And there is error
[ 50%] Building CXX object CMakeFiles/ass_lab_2.dir/main.cpp.o
[100%] Linking CXX executable ass_lab_2
/usr/bin/ld: CMakeFiles/ass_lab_2.dir/main.cpp.o: relocation R_X86_64_32S against undefined symbol `x' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
...
P.S. I solved problem. This link was extremely helpful. You just need to pass your variables into asm function using it's syntax.
(Editor's note: the first couple pages of that, about using GNU C Basic asm statements to modify global variables, are unsafe (because there's no "memory" clobber) and only happen to work. The only safe way to modify C variables is with Extended asm with input/output operands; later sections of that article cover that.)
Your main issue is that x is a local variable. You will have to use extended assembly to modify it (and use -masm=intel to use Intel syntax):
int main(void)
{
short int x = 0;
__asm__("mov %0, 0x02\n\t" : "=r"(x));
printf("%d", x);
}
Also, you can use AT&T syntax. It will look like this:
int main(void)
{
short int x = 0;
__asm__("mov $0x02, %0\n\t" : "=r"(x));
printf("%d", x);
}
Because I'm using the =r constraint here, this will be stored in a register; therefore, you don't need to use eax (which should be ax, by the way) as an intermediate storage place to store the value.

global variable always initialized zero

I've been writing an OS using this tutorial. I am at the part where
the boot loader is completed and C is used for programming (and then linked together ...). But that just as a note, I believe the problem I have is related to gcc.
I build an i386-elf cross compiler for the OS. And everything works fine, I can execute my code everything works. Except that all global variables are initialized zero, although I provided a default value.
int test_var = 1234;
// yes, void main() is correct (the boot-loader will call this)
void main() {}
If I debug this code with GDB, I get: (gcc-7.1.0, target: i328-elf)
(gdb) b main
Breakpoint 1 at 0x1554: file src/kernel/main.c, line 11.
(gdb) c
Continuing.
Breakpoint 1, main () at src/kernel/main.c:11
11 void main() {
(gdb) p test_var
$1 = 0
If i run the same code on my local machine (gcc-6.3.0, target: x86_64), it prints 1234.
My question is: Did I misconfigure gcc, is this a mistake in my OS, is this a known problem? I couldn't find anything about it.
My entire source-code: link
I use the following commands to compile my stuff:
# ...
i386-elf-gcc -g -ffreestanding -Iinclude/ -c src/kernel/main.c -o out/kernel/main.o
# ...
i386-elf-ld -e 0x1000 -Ttext 0x1000 -o out/kernel.elf out/kernel_entry.o out/kernel/main.o # some other stuff ...
i386-elf-objcopy -O binary out/kernel.elf out/kernel.bin
cat out/boot.bin out/kernel.bin > out/os.bin
qemu-system-i386 -drive "format=raw,file=out/os.bin"
EDIT: As #EugeneSh. suggested here some logic to make sure, that it's not removed:
#include <cpu/types.h>
#include <cpu/isr.h>
#include <kernel/print.h>
#include <driver/vga.h>
int test_var = 1234;
void main() {
vga_text_init();
switch (test_var) {
case 1234: print("That's correct"); break;
case 0: print("It's zero"); break;
// I don't have a method like atoi() in place, I would use
// GDB to get the value
default: print("It's something else");
}
}
Sadly it prints It's zero
Compiler never clears uninitialized global variables to zero, its logic in built inside loader,
So when you allocate memory for data segment then it size contains bss section also. So you have to check bss section offset, alignment & size withing data segment and memset() them to '0'.
As you are writing your OS so may be all the library routines are not available so better write memset() function using assembly.

Referencing symbols in other files using PPC assembly

How can you reference a symbol external to the current file in ppc assembly? I've tried looking at the .extern keyword as well as adding a new symbol in the linker file but with no success.
I have two ppc assembly files that are part of a larger project. I wish to reference a symbol (__head) in file1 from file2 in this way:
file1.S:
.section ".head","ax"
. = 0
.global __head
__head:
file2.S:
.section ".head","ax"
...
LOAD_32(%r3, file2_symbol_name - __head)
where LOAD_32 is
#define LOAD_32(r, e) \
lis r,(e)#h; \
ori r,r,(e)#l;
...but am getting the following error:
file2.S: Assembler messages:
file2.S:113: Error: can't resolve `file2_symbol_name' {.head section} - `__head' {*UND* section}
file2.S:113: Error: expression too complex
When used in file1 LOAD_32(%r3, file1_symbol_name - __head) works just fine so I know I'm not importing the symbol name correctly. How can I do this?
EDIT:
I have reduced my problem to the bare minimum parts so that I am clear about the problem. Below is all of the code, linker file, Makefile, and the terminal output for "make quick".
NB: When I comment out line 9 of other.S the project compiles without error.
head.S:
#include "asm-defines.h"
.section ".head","ax"
.align 0x10
. = 0x0
.global __head
__head:
LOAD_32(%r3, file1_symbol_name - __head)
b .
file1_symbol_name:
b .
other.S
#include "asm-defines.h"
.section ".head","ax"
.align 0x10
.global other
other:
LOAD_32(%r3, file2_symbol_name)
LOAD_32(%r3, file2_symbol_name - __head)
b .
file2_symbol_name:
b .
asm-defines.h:
#ifndef ASM_DEFINES_H
#define ASM_DEFINES_H
/* Load an immediate 32-bit value into a register */
#define LOAD_32(r, e) \
lis r,(e)#h; \
ori r,r,(e)#l;
#endif //ASM_DEFINES_H
quick.lds
ENTRY(__head);
Makefile
CC=$(CROSS)gcc
QFLAGS := -Wl,--oformat,elf64-powerpc -pie -m64 -mbig-endian -nostdlib
quick:
$(CC) $(QFLAGS) -T quick.lds head.S other.S -o quick.o
$(CROSS) is a path to the cross compiler which I have omitted.
The CC is powerpc64le-buildroot-linux-gnu-gcc
Terminal
$ make quick
powerpc64le-buildroot-linux-gnu-gcc -Wl,--oformat,elf64-powerpc -pie -m64 -mbig-endian -nostdlib -T quick.lds head.S other.S -o quick.o
other.S: Assembler messages:
other.S:9: Error: can't resolve `.head' {.head section} - `__head' {*UND* section}
other.S:9: Error: expression too complex
other.S:9: Error: can't resolve `.head' {.head section} - `__head' {*UND* section}
other.S:9: Error: expression too complex
make: *** [quick] Error 1
The assembler cannot know the placement / relative position of head.S and other.S at assembly time to be able to compute the relative displacement of the file2_symbol_name and __head labels. This is a general assembly language question, not PPC specific.
Regarding David's response, see https://sourceware.org/binutils/docs/as/Infix-Ops.html#Infix-Ops, where it specifies: "Subtraction. If the right argument is absolute, the result has the section of the left argument. If both arguments are in the same section, the result is absolute. You may not subtract arguments from different sections." The error message indicates that the assembler has no idea which section contains __head, which indeed it cannot, since the symbol and its section are not defined in filescope.
You might be able to get what you want by using the .weak and/or .weakref directives so that the symbol can be defined in two files, with the strong reference overriding the weak one at link time. I haven't experimented with this. See the manual (https://sourceware.org/binutils/docs/as/) and hunt for .weak.
Some background information on weak symbols is here: https://en.wikipedia.org/wiki/Weak_symbol.

Error: L6218E: Undefined symbol three()

[Edit: The question is flawed, the file I described as "main.c" was actually "main.cpp" and that it why I was having an issue, calling a C function from a C++ file. The question is thus incorrect and doesn't have an answer, but if you have this undefined symbol issue, also think about checking you're not mixing C & C++.]
I'm using uVision 5 to develop a firmware, however I can't get the linker to find one of my functions.
main.c :
#include "Test.h"
int main()
{
return three();
}
Test.h :
#ifndef TEST_H
#define TEST_H
int three();
#endif
Test.c
#include "Test.h"
int three()
{
return 3;
}
All those files are at the root of my project, I know they get compiled as if I introduce a syntax error in them, compiler reports an error.
Also looking at the map file produced, I see that three() was removed:
Removing test.o(i.three), (4 bytes).
For testing purposes, I had --no_remove to linker command line, map file now contains:
0x0002ba76 0x00000004 Code RO 1 i.three test.o
So obviously, the linker is well aware of my function, and will or won't remove it depending on flags.
Regardless, it reports:
.\build\uvision5\test.axf: Error: L6218E: Undefined symbol three() (referred from main.o).
Not enough information to list image symbols.
Flawed question, it was actually a case of mixing C/C++, in which case you'll get a symbol missing if you call a C function from C++ without declaring it extern C.

How to place a variable at a given absolute address in memory (with GCC)

The RealView ARM C Compiler supports placing a variable at a given memory address using the variable attribute at(address):
int var __attribute__((at(0x40001000)));
var = 4; // changes the memory located at 0x40001000
Does GCC have a similar variable attribute?
I don't know, but you can easily create a workaround like this:
int *var = (int*)0x40001000;
*var = 4;
It's not exactly the same thing, but in most situations a perfect substitute. It will work with any compiler, not just GCC.
If you use GCC, I assume you also use GNU ld (although it is not a certainty, of course) and ld has support for placing variables wherever you want them.
I imagine letting the linker do that job is pretty common.
Inspired by answer by #rib, I'll add that if the absolute address is for some control register, I'd add volatile to the pointer definition. If it is just RAM, it doesn't matter.
You could use the section attributes and an ld linker script to define the desired address for that section. This is probably messier than your alternatives, but it is an option.
Minimal runnable linker script example
The technique was mentioned at: https://stackoverflow.com/a/4081574/895245 but now I will now provide a concrete example.
main.c
#include <stdio.h>
int myvar __attribute__((section(".mySection"))) = 0x9ABCDEF0;
int main(void) {
printf("adr %p\n", (void*)&myvar);
printf("val 0x%x\n", myvar);
myvar = 0;
printf("val 0x%x\n", myvar);
return 0;
}
link.ld
SECTIONS
{
.mySegment 0x12345678 : {KEEP(*(.mySection))}
}
GitHub upstream.
Compile and run:
gcc -fno-pie -no-pie -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out
Output:
adr 0x12345678
val 0x9abcdef0
val 0x0
So we see that it was put at the desired address.
I cannot find where this is documented in the GCC manual, but the following syntax:
gcc link.ld main.c
seems to append the given linker script to the default one that would be used.
-fno-pie -no-pie is required, because the Ubuntu toolchain is now configured to generate PIE executables by default, which leads the Linux kernel to place the executable on a different address every time, which messes with our experiment. See also: What is the -fPIE option for position-independent executables in gcc and ld?
TODO: compilation produces a warning:
/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?
Am I doing something wrong? How to get rid of it? See also: How to remove warning: link.res contains output sections; did you forget -T?
Tested on Ubuntu 18.10, GCC 8.2.0.
You answered your question,
In your link above it states:
With the GNU GCC Compiler you may use only pointer definitions to access absolute memory locations. For example:
#define IOPIN0 (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;
Btw http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes
Here is one solution that actually reserves space at a fixed address in memory without having to edit the linker file:
extern const uint8_t dev_serial[12];
asm(".equ dev_serial, 0x1FFFF7E8");
/* or asm("dev_serial = 0x1FFFF7E8"); */
...
for (i = 0 ; i < sizeof(dev_serial); i++)
printf((char *)"%02x ", dev_serial[i]);
In GCC you can place variable into specific section:
__attribute__((section (".foo"))) static uint8_t * _rxBuffer;
or
static uint8_t * _rxBuffer __attribute__((section (".foo")));
and then specify address of the section in GNU Linker Memory Settings:
.foo=0x800000
I had a similar issue. I wanted to allocate a variable in my defined section at a special offset. In the same time I wanted the code to be portable (no explicit memory address in my C code). So I defined the RAM section in the linker script, and defined an array with the same length of my section (.noinit section is 0x0F length).
uint8_t no_init_sec[0x0f] __attribute__ ((section (".noinit")));
This array maps all locations of this section. This solution is not suitable when the section is large as the unused locations in the allocated array will be a wasted space in the data memory.
The right answer to my opinion is the Minimal runnable linker script example one.
However, there was something not mentioned there:
If the variable is not used in code (e.g. the variable holds read-only data such as version...), it is necessary to add the 'used' attribute.
Refer to my answer at https://stackoverflow.com/a/75468786/3887115.

Resources