0

tl;dr I am getting a few different errors depending on how I try to call a C _print function from x86-64 assembly. I would like to get it printing by calling the C function, so I know I can call C functions, but I'm not sure how to do "stack alignment" to make that happen properly. The full source code for the system is included.


I tried following these instructions but I am getting this:

Segmentation fault: 11

My assembly looks like this:

section .text

global start
extern _print

start:
  mov rdi, msg
  jmp _print

section .data

msg: db  0xa, "  Hello StackOverflow!!!", 0xa, 0xa, 0
.len: equ $ - msg

My print.c function is like this:

#import <stdio.h>

extern
void
print(char *str) {
  puts("FOO");
  puts(str);
}

I am able to use the print function in C directly, so I know that works. So I should see FOO in the output but I just see something like this followed by an error:

Hello StackOverflow!!@���

I compiled the C project like this:

print:
  @clang -I . -dynamiclib \
    -O2 \
    -undefined dynamic_lookup \
    -o print.dylib print.c
.PHONY: print

And I compiled the asm project like this:

asm:
  @nasm -f macho64 main.asm
  @ld -macosx_version_min 10.13.0 \
    -lSystem -o main \
    -install_name print.dylib \
    main.o print.dylib
  @./main
.PHONY: asm

I don't understand how to apply this stack frame / alignment concept to the 64-bit architecture.

Tinkering around and I tried changing start to this. I have no idea why (don't understand how to apply the stack alignment):

start:
  push rbx
  mov rdi, msg
  call _print
  pop rbx

It ends up outputting this:

  Hello StackOverflow!!@���make: *** [asm] Error 109

Still no FOO from the print as well.

Lance
  • 75,200
  • 93
  • 289
  • 503
  • `int len = sizeof(str);` I think you mean `strlen(str)+2`. – Nate Eldredge Mar 22 '19 at 21:22
  • Of course, you can do the whole thing more simply, and avoid the need for the extra array, with `printf("FOO%s\n", str);` – Nate Eldredge Mar 22 '19 at 21:23
  • I'm trying my best to avoid the `printf` and `sprintf` functions because they do too much. Still have to remove that last little bit. – Lance Mar 22 '19 at 21:24
  • So then, `puts("FOO"); puts(str); puts("\n");` – Nate Eldredge Mar 22 '19 at 21:25
  • Perfect, thanks going to do that! But beside the point :) – Lance Mar 22 '19 at 21:27
  • Well, yes and no; I think the segfault is because you made the `out` array too small and overran it. And because of buffering, you also wouldn't see the FOO be output if your program crashes before the later `printf`s. So fixing that may get you closer than you think. – Nate Eldredge Mar 22 '19 at 21:29
  • Oh, `puts` has a trailing newline so maybe you want `fputs(..., stdout)` instead. – Nate Eldredge Mar 22 '19 at 21:29
  • As for the macro issue, I would guess that nasm doesn't expand macro parameters within a token such as in `_%1`. A glance at the manual suggests that `_ %+ %1` may work but I haven't tried it. – Nate Eldredge Mar 22 '19 at 21:32
  • 1
    But I really don't see the point of your macro. You're defining a "helper" function `print` whose only job is to call `_print`. It doesn't align the stack correctly and it doesn't return properly. Compared to just calling `_print` yourself, this just wastes cycles and complicates your program. – Nate Eldredge Mar 22 '19 at 21:45
  • Actually, there are quite a few other problems with what you're trying to do. You are making your assembler routine the entry point of the program, which means that the standard C library's startup code never gets to run. As such, you can't expect that calls to C library functions, e.g. stdio, are going to work properly. Another issue is that after your call to `_print` returns, execution just runs off the end of `start`. You need to write code to invoke the `_exit(2)` system call or your program is going to die by segfault. – Nate Eldredge Mar 22 '19 at 21:49
  • I have a lot of functions that will go through the macro, let's say a few hundred. I don't like the _prefix underscore and I can't figure out how to [get rid of it](https://stackoverflow.com/questions/1034852/adding-leading-underscores-to-assembly-symbols-with-gcc-on-win32/1035937#1035937) in clang, but I want to export the assembly library with `print` not `_print` so you can use it in another c or assembly project. – Lance Mar 22 '19 at 21:55
  • Running again a bunch of times it gives `Error 12, Error 16, Error 17, Error 18, Error 20, ... Error 21`. Well _that's_ helpful haha. – Lance Mar 22 '19 at 21:57
  • So, I think the first question to be resolved is: do you want the C library startup code to run, or not? If no, you can keep your entry point called `start`, but you'll have to resign yourself to not being able to safely call any nontrivial C library functions, e.g. `fputs` or `printf`, and you'll have to do I/O yourself with lower level system calls like `write(2)`. You'll also need to exit manually. But if yes, you should make your entry point be a label called `_main` and expect it to be called with C conventions, and you can exit your program by simply returning. – Nate Eldredge Mar 22 '19 at 22:01
  • If I try to rename the function name in compiler output according to [this](https://stackoverflow.com/questions/1034852/adding-leading-underscores-to-assembly-symbols-with-gcc-on-win32/1035937#1035937), as in `extern func () asm ("FUNC");`, I get `error: invalid instruction mnemonic 'FUNC'`. – Lance Mar 22 '19 at 22:01
  • I want to use only system calls if possible so what you're saying sounds good, however low-level it can get. – Lance Mar 22 '19 at 22:02
  • `asm("FUNC")` is a gcc feature so I wouldn't be surprised if it doesn't work with clang. – Nate Eldredge Mar 22 '19 at 22:03
  • 3
    Any standard library symbol names will have a leading underscore, you can't avoid that. (You could for libraries you create yourself, though). But anyway, instead of a runtime wrapper (which should be a `jmp` tailcall, not another `call` + `ret` which breaks stack args and stack alignment), just use **`%define print _print`** Or make your `lib` macro do `%define %1 _%1`. – Peter Cordes Mar 22 '19 at 22:56
  • @PeterCordes what about exporting the functions in the assembly project so you can use it in another assembly or c project. I don't want to have to use it there with underscores if at all possible. – Lance Mar 22 '19 at 23:14
  • @PeterCordes changing it to `start: mov rdi, msg jmp print` results in `Hello StackOverflow!!P���make: *** [asm] Error 30` It should show `FOO` I don't understand. – Lance Mar 22 '19 at 23:17
  • 1
    If you want to call it from C, you need the `_print` names because that's part of the platform name-mangling convention. If you want to use it from another NASM project, you need `extern` declarations anyway, so you might as well have a NASM "header" file that you `%include` which uses extern and `%define` for each library function. (Really I'd suggest just getting used to it: `_name` functions can be a handy reminder that the're written in C and use the C calling convention, while other names might be asm with a custom calling convention.) – Peter Cordes Mar 22 '19 at 23:17
  • @LancePollard /facepalm. I meant change the `call` *inside the wrapper function* to `jmp`, to make it truly a transparent wrapper that forwards all its args without messing up the stack. I think you still haven't realized that `call` at the bottom of that wrapper function will return *there*. Think about what return address will be pushed by a `call` from different places. But using `%define` is an even better alternative than calling to a `jmp` instruction anyway. – Peter Cordes Mar 22 '19 at 23:21
  • I updated the question based on the feedback, removed the macro stuff since it was unnecessary. Mainly I just want to be able to call a C function I made like that. – Lance Mar 23 '19 at 01:28
  • Your Makefile is basically defeating the purpose of dependencies. Your print target should be `print.dylib: print.c`, and `asm` should be `main: main.asm print.dylib`. Then you don't need a `.PHONY` because your rules accurately describe what you're building. (And `make main` will rebuild the C library if it's out of date, too.) You could break things down more (into `.o` dependencies) to allow `make -j` to build the library and run NASM in parallel. – Peter Cordes Mar 23 '19 at 01:38
  • Does it help if you `#include ` in your `print.c`? As is, it compiles but with a warning about ```implicit declaration of function ‘puts’```, so you should probably fix that! Also, on OS X, are you sure it's safe to call libc functions from `start`, not `main`? Or do dynamic-library hooks let libc run its init function before execution reaches the top of `start`, like on Linux? You're returning from `start` (by tail-calling a function that will eventually `ret`), not making an `_exit` system call or calling `exit` in libc, so maybe it's not the entry point at all? I don't have a Mac – Peter Cordes Mar 23 '19 at 01:45
  • 1
    Anyway, your code works for me on Linux, `gcc -O2 -shared lib.c -o libprint_lib.so`, assemble with `nasm -felf64 p.asm`, link with `gcc -nostartfiles p.o -L/tmp -lprint_lib` to create a dynamically linked executable. (I had to change the source to `call print wrt ..plt` for it to link into a PIE executable.) Runs with `LD_LIBRARY_PATH=/tmp ./a.out` and prints both `FOO\n` and `\nHello...\n\n`, then segfaults after execution falls off the end of `start`. So you're probably building it wrong. – Peter Cordes Mar 23 '19 at 01:54
  • What happens if you do `ld -macosx_version_min 10.7.0` instead of `ld -macosx_version_min 10.13.0` – Michael Petch Mar 23 '19 at 03:03
  • @MichaelPetch it gives `Undefined symbols for architecture x86_64: "start", referenced from`... – Lance Mar 23 '19 at 03:14
  • Are you using the code in your question or something else? – Michael Petch Mar 23 '19 at 03:14
  • The makefile is the same, but I was using slightly evolving code debugging (c is large file). But copy-pasting the asm above gives `Segmentation fault: 11`. – Lance Mar 23 '19 at 03:16
  • I can understand you getting `Segmentation Fault` since you don't use an exit system call to exit your program after returning from `print` in function `start`. Did it print FOO and Hello World before faulting? – Michael Petch Mar 23 '19 at 03:17
  • Going back to 10.13 and changing `start` to `_main` gives `Hello StackOverflow!!P���make: *** [...] Error 30`. I removed all includes/macros. – Lance Mar 23 '19 at 03:17
  • No it doesn't print anything on 10.7. – Lance Mar 23 '19 at 03:17
  • What version of `start` are you using. The one with `jmp` or the one with the `call` surrounded by push/pop? – Michael Petch Mar 23 '19 at 03:18
  • The one with jmp. – Lance Mar 23 '19 at 03:18
  • Have you stepped through your code with the `lldb` debugger? – Michael Petch Mar 23 '19 at 03:19
  • And when you said it didn't print anything were you using 10.7.0 or 10.13.0? – Michael Petch Mar 23 '19 at 03:20
  • No I didn't know of that. That might be good. With push/pop start and 10.7 it prints ` Hello StackOverflow!!P��� make: *** [...] Segmentation fault: 11`. On 10.7 with jmp it doesn't print. – Lance Mar 23 '19 at 03:20
  • If you send me a copy of your program `main` to mpetch@gmail.com I may be able to look at it. – Michael Petch Mar 23 '19 at 03:35

0 Answers0