2

Since a few days, I've started to learn Assembly, using a book I've gotten as a present, and I got an issue.

I'm trying to access a C++ global variable from within Assembly, but I keep getting a Linking Error, saying relocation R_X86_64_32S against undefined symbol '_symbol' can not be used when making a PIE object; recompile with -fPIE.

I've tried several things already, like changing the extern to global in my asm file, and vice versa. I've also tried removing the underscore, adding an underscore in both Assembly and C++, but I can't manage to work it out...

Here's my Assembly code:

;---------------------
;      Functions
;---------------------
global IntegerLogical_

;----------------------------
;      Global Variables
;----------------------------
extern _symbol  ; Defines the symbol C/C++ Global Variable as a dword (unsigned int)

IntegerLogical_:
    and ecx, edx                ; a & b
    or  ecx, r8d                ; (a & b) | c
    xor ecx, r9d                ; ((a & b) | c) ^ d
    add ecx, [_symbol]            ; (((a & b) | c) ^ d) + g_Val1

    mov eax, ecx                ; Move the return value into the EAX Register
    ret                         ; Return to caller

And here's my C++ code:

#include <iostream>
#include <iomanip>

extern "C" unsigned int IntegerLogical_(unsigned int a, unsigned int b, unsigned int c, unsigned int d);
extern "C" unsigned int symbol = 0;

unsigned int integerLogicalCpp(unsigned int a, unsigned int b, unsigned int c, unsigned int d) {
    // Calculate (((a & b) | c) ^ d) + symbol
    unsigned int t1 = a & b;
    unsigned int t2 = t1 | c;
    unsigned int t3 = t2 ^ d;
    
    return t3 + symbol;
}

void printResult(const char* message, unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int val1, unsigned int r1, unsigned int r2) {
    const int w = 8;

    std::cout << message << std::endl;
    std::cout << std::setfill('0');
    std::cout << "a =    0x" << std::hex << std::setw(w) << a << " (" << std::dec << a << ")" << std::endl;
    std::cout << "b =    0x" << std::hex << std::setw(w) << b << " (" << std::dec << b << ")" << std::endl;
    std::cout << "c =    0x" << std::hex << std::setw(w) << c << " (" << std::dec << c << ")" << std::endl;
    std::cout << "d =    0x" << std::hex << std::setw(w) << d << " (" << std::dec << d << ")" << std::endl;
    std::cout << "val1 = 0x" << std::hex << std::setw(w) << val1 << " (" << std::dec << val1 << ")" << std::endl;
    std::cout << "r1 =   0x" << std::hex << std::setw(w) << r1 << " (" << std::dec << r1 << ")" << std::endl;
    std::cout << "r2 =   0x" << std::hex << std::setw(w) << r2 << " (" << std::dec << r2 << ")" << std::endl;
    std::cout << std::endl;

    if (r1 != r2) std::cout << "Comparison failed! Values do not match! (" << r1 << " <-> " << r2 << ")" << std::endl;
}

int main() {
    unsigned int a = 0x00223344;
    unsigned int b = 0x00775544;
    unsigned int c = 0x00555555;
    unsigned int d = 0x00998877;
    symbol = 7;
    unsigned int r1 = integerLogicalCpp(a, b, c, d);
    unsigned int r2 = IntegerLogical_(a, b, c, d);
    printResult("Test 1", a, b, c, d, symbol, r1, r2);

    a = 0x70987655;
    b = 0x55555555;
    c = 0xAAAAAAAA;
    d = 0x12345678;
    symbol = 23;
    r1 = integerLogicalCpp(a, b, c, d);
    r2 = IntegerLogical_(a, b, c, d);
    printResult("Test 2", a, b, c, d, symbol, r1, r2);
}

I'm new to this, so I might just have made a stupid mistake, if so, please tell me. I'll also supply some extra information that might be of use.

NASM Build Instruction: nasm -f elf64 -o [Assembled file].o [Assembly file].asm G++ Build Instruction: g++ [Assembled file].o [C++ Source File].cpp -o [Output]

G++ Output:

[C++ Source File].cpp:5:25: warning: ‘symbol’ initialized and declared ‘extern’
    5 | extern "C" unsigned int symbol = 0;
      |                         ^~~~~~
/usr/bin/ld: [Assembled file].o: relocation R_X86_64_32S against undefined symbol `_symbol' can not be used when making a PIE object; recompile with -fPIE
collect2: error: ld returned 1 exit status

I'm using NASM 2.15.05 and G++ 11.1.0. Edit: I'm programming in an x64 Linux Environment

Thanks in advance, and enjoy the rest of your day.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • What operating system are you programming for? – fuz Jan 03 '22 at 17:26
  • I'm programming for Linux x64 – TheAlgorithm476 Jan 03 '22 at 17:27
  • You have two options: either link with `-no-pie` or change your code to retrieve the variable's address through the GDT. To see how this is done, compile a C file that accesses an external global variable and copy the method used by the C compiler. – fuz Jan 03 '22 at 18:44
  • `R_X86_64_32S` is #defined as *Direct 32 bit sign extended*. Try to get rid of this relocation type replacing `add ecx, [_symbol]` by `lea r10,[_symbol]` `add ecx,[r10]`. – vitsoft Jan 03 '22 at 18:46
  • @fuz Thanks for the advice, however, it did not work. The method used by the C Compiler somehow didn't work? And about the -no-pie linking method, it does link correctly, but it doesn't pass my tests, meaning C++ and Assembly do the same thing, but with another outcome... – TheAlgorithm476 Jan 03 '22 at 19:03
  • @vitsoft Thanks for you advice as well, I did what you said, but end up with the same error. Seems like everything gets compiled and assembled just fine, it's just the linker that doesn't seem to be liking what I'm doing... – TheAlgorithm476 Jan 03 '22 at 19:04
  • @TheAlgorithm476 Please show what you tried when you copied the C compiler's method. – fuz Jan 03 '22 at 19:39
  • @fuz I used the source code I had lying around from a previous chapter of the book I mentioned, because it used a global variable as well, and that program worked fine. I compiled it using GCC, and then did an objdump of it, to get the disassembly. From there on, I honestly don't know what I did anymore. Was this what you talked about, or am I doing it completely wrong? – TheAlgorithm476 Jan 03 '22 at 19:58
  • @TheAlgorithm476 Use the `-S` flag to obtain assembly code. If you like, I can give you an example for how to access global variables from PIC. – fuz Jan 03 '22 at 20:08
  • @vitsoft What is that supposed to achieve? The addressing mode in your example is the exact same. This advice is not helpful. – fuz Jan 03 '22 at 20:09
  • @fuz I'd love to get a working example, I've been looking at this for hours now, and I really can't find a solution in this. It would help out a ton. I've also noticed that @vitsoft's advice doesn't work. It did help me in a way however, I now know what the `lea` instruction is, so I take that one for granted :) – TheAlgorithm476 Jan 03 '22 at 20:21
  • 1
    @TheAlgorithm476 Okay, I'll write an answer. May take a while as I'm bogged down doing the taxes of our club right now. – fuz Jan 03 '22 at 21:33
  • 3
    @fuz: This asm source is going to be linked into the same executable as the C; no need for indirection through the GOT. The asm just needs `default rel`, to make `add eax, [symbol]` assemble as `[rel symbol]`. And to use the proper asm symbol name, without a leading underscore since this is Linux/ELF, not MacOS. – Peter Cordes Jan 03 '22 at 22:19
  • @vitsoft: Why would you want to LEA a pointer into R10? LEA isn't the only instruction that can use a RIP-relative addressing mode; it's a standard ModRM encoding in 64-bit mode, so `add ecx, [rel symbol]` works as a drop-in replacement. (Also, as Fuz said, you used the same non-working addressing mode for LEA that the question used for ADD, so you're not even using a RIP-relative LEA, just a 32-bit-absolute LEA.) – Peter Cordes Jan 03 '22 at 22:39
  • @PeterCordes Oh I totally forgot about that one. You are probably correct and `default rel` should do the trick. – fuz Jan 03 '22 at 23:24
  • @PeterCordes I wanted `LEA R10,[_symbol]` assembled to `4C8D15(00000000)` which produces `R_X86_64_PC32` (instead of `R_X86_64_32S` which is OP unable to link). I didn;t realize that relative addressing is not default in NASM. – vitsoft Jan 04 '22 at 07:06

0 Answers0