Referencing symbols in other files using PPC assembly - c

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.

Related

Linker error using program counter on Apple Silicon

Following the HelloSilicon tutorial on ARM assembly for Apple Silicon I decided to play around with the general purpose registers and with the program counter.
When feeding the source to the linker with any of the registrers ("R1", "R2"... ) or "PC" ("R15") I got the following linker error:
% ld -o cicle cicle.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch arm64
Undefined symbols for architecture arm64:
"PC", referenced from:
_start in cicle.o
ld: symbol(s) not found for architecture arm64
The source is:
.global _start // Provide program starting address to linker
.align 2 // Make sure everything is aligned properly
// Setup the parameters to print hello world
// and then call the Kernel to do it.
_start:
label:
mov X0, #1 // 1 = StdOut
adr X1, helloworld // string to print
mov X2, #13 // length of our string
mov X16, #4 // Unix write system call
svc #0x80 // Call kernel to output the string
b PC //Expecting endless loop
//b PC-6 //Expecting endless "Hello World" loop
// Setup the parameters to exit the program
// and then call the kernel to do it.
mov X0, #0 // Use 0 return code
mov X16, #1 // System call number 1 terminates this program
svc #0x80 // Call kernel to terminate the program
helloworld: .ascii "Hello World!\n"
I understand that this problem can be solved when coding on XCode 12+ changing the Bundle to Mach-O on the build settings, however, appending the bundle argument -b sends a ld: warning: option -b is obsolete and being ignored warning.
When feeding the source to the linker with any of the registrers ("R1", "R2"... ) or "PC" ("R15") I got the following linker error:
These registers don't exist, you're reading the manual for the wrong architecture. You need ARMv8, not ARMv7.
As for this:
b PC //Expecting endless loop
//b PC-6 //Expecting endless "Hello World" loop
PC is not a thing. It's just an identifier like any other, such as _start. If you want to refer to the current instruction, use a dot (.):
b . // infinite loop
b .+8 // skip the next instruction
Note that any arithmetic is unscaled. If you want to jump 6 instructions back, you'll have to do b .-(4*6). If you attempted to compile b .-6, you'd get an error because b is only able to encode offsets aligned to 4 bytes.

Stack initialisation in GNU ARM toolchain

Checking the startup file provided as an example in the GNU ARM toolchain, I couldnt understand one thing.
Code snippets provided here are taken from examples included in GNU ARM Embedded Toolchain files downloaded from official website. Code compiles and everything seems to be good.
I am wondering why they wrote this code exactly like that, why they are using same names for example?
I am wondering why my linker is not complaining about multiple definition error for __StackTop and __StackLimit. Here is the part of the file startup_ARMCM0.S
.syntax unified
.arch armv6-m
.section .stack
.align 3
#ifdef __STACK_SIZE
.equ Stack_Size, _*emphasized text*_STACK_SIZE
#else
.equ Stack_Size, 0xc00
#endif
.globl __StackTop
.globl __StackLimit
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
__StackTop:
.size __StackTop, . - __StackTop
If the linker is defining the same symbols: __StackTop and __StackLimit.
.stack_dummy (COPY):
{
*(.stack*)
} > RAM
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
While checking linker documentation, it was written that, given the example:
SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
In this example, if the program defines _etext (with a leading
underscore), the linker will give a multiple definition error. If, on
the other hand, the program defines etext (with no leading
underscore), the linker will silently use the definition in
the program. If the program references etext but does not define
it, the linker will use the definition in the linker script.
Also, when using readelf -s just to check symbols generated from assembly file startup_ARMCM0.S without linking, I can see the symbol __StackTop and __StackLimit with one values. While, after linking they have the values set up by the linker (keeping in mind that the value of the linker is actually stored in address of the symbol).

One or two weird linking errors with some inline assembly

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).

How can the C function "Exp" be properly used in NASM for Linux?

I am trying to implement the C function "exp" in NASM for Linux. The function takes a double value x, and returns a double value r = e^x, where e is Euler's Number. This is my implementation:
extern exp
SECTION .bss
doubleActual: resq 1
doubleX: resq 1
SECTION .text
main:
;some other code here
;calculate actual result
push doubleActual ; place to store result
push doubleX ;give the function what x is.
call exp
add esp, 8
On compile attempt, i get the following:
hw7_3.o: In function `termIsLess':
hw7_3.asm:(.text+0xf9): undefined reference to `exp'
This is referring to when i actually call exp, which is odd, because "extern exp" seems to work just fine. What am i doing incorrectly?
via http://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_17.html ....
I need to do the following with gcc:
gcc -m32 name.o -lm -o name
The "-lm" tag is a shortcut to link the C math library, which is separate from the standard library.

Undefined reference to multiply

I'm trying to call C function in assembler. This is my code:
C:
int multiply(int what)
{
return what * 2;
}
ASM:
extern multiply
start:
mov eax, 10
push eax
call multiply
jmp $
;empty
times 510-($-$$) db 0
dw 0xAA55
I'm compiling C code to elf by gcc (MinGW) and ASM code by NASM. I'm compiling it without any problems, but when I'm trying to use this code(for creating .bin file):
gcc -o test.bin work.o test.o
I' getting this error:
Does anybody know how to call C function from ASM code, compile it and link it to working .bin file? Please help.
Try to add '_' to multiply:
extern _multiply
Works for me in this simple example:
global _main
extern _printf
section .data
text db "291 is the best!", 10, 0
strformat db "%s", 0
section .code
_main
push dword text
push dword strformat
call _printf
add esp, 8
ret
Try "global multiply" instead of "extern multiply" in your .asm file. You shouldn't need the underscore for ELF (I don't think), but you can get Nasm to automagically add an underscore to anything "extern" or "global" by adding "--prefix _" to Nasm's command line.
Edit: I take that back, "extern" is correct. You seem not to have a "main". Try adding "--nostartfiles"
(may be only one hyphen) to gcc's command line.
Best,
Frank

Resources