1

Let's say I have the following program (a.c):

#include <stdio.h>

void f()
{
    printf("Hello, world!");
}

int main(void)
{
    f();
    return 0;
}
$ gcc -g a.c

Having a.out, how do I find out where main() is defined? I mean, in a big project it's not always clear where main() comes from.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
  • 2
    Since you used tags like "nm" and "objdump", I suppose you already know the answer to your question? – anatolyg Jan 02 '22 at 18:46
  • `in a big project it's not always clear where main() comes from` `grep -R main` – KamilCuk Jan 02 '22 at 18:49
  • @anatolyg I know they can give an answer, but I don't know how to interpret their output. – x-yuri Jan 02 '22 at 22:08
  • @KamilCuk Let me [show](https://gist.github.com/x-yuri/df02228f4751a80b8eb5635c67752c33) you. – x-yuri Jan 02 '22 at 22:12
  • ? Then `grep -R main | grep -v configure | grep -v aclocal` or use that `--include=*.c` option. Also `grep -R 'int[[:space:]]*main[[:space:]]*\('` or similar. I guess main is here https://github.com/freedesktop/xorg-xserver/blob/aeed57d722f2eb978c17fd7e859334d34af38d05/dix/stubmain.c#L32 . Also ctags, cscope, clangd and similar tools. – KamilCuk Jan 02 '22 at 22:25
  • @KamilCuk All those tools, they are viable alternatives. And generally *they* should be used. But to find `main()`... Depending on the size of a project, your experience, an extent to which you know the project, they might not work very well. E.g. with `ctags` I have 17 possible locations. I like your `gdb` solution more. At least in this case, for preciseness. By the way, your guess seems to be correct. – x-yuri Jan 03 '22 at 09:56
  • In all modern programming IDEs you have always the option to go from some code calling the function to the function definition. They also have a "find in project" option. None but masochists programs in pure console crap any longer, so I don't really understand the problem. – Lundin Jan 04 '22 at 10:50
  • Okay, the question was apparently put too broadly. Although I mentioned in the question that I'm concerned about `main()` and big projects. @Lundin The problem is, clone [`xserver`](https://gitlab.freedesktop.org/xorg/xserver) (at the moment you might need a different [clone url](https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/407#note_1206088)), and find `main()`. About masochists, I provided simple instructions to not list 10 steps to reproduce it in what you supposedly call a modern IDE. And I guess "modern IDEs" have different defaults (compile options). – x-yuri Jan 04 '22 at 11:40
  • What do you mean "IDE's have different compile options". Your _projects_ have different compile options and it's your responsibility to set them, not the IDE... – Lundin Jan 04 '22 at 12:16
  • @Lundin I mean default compile options. When you create a project, does it ask you for compile options? Are default compile options the same across all the IDE's? – x-yuri Jan 04 '22 at 15:07
  • Who cares, gcc default options are bad so you need to set them manually always and likely different between debug and release build too. Searching for identifiers is the job of the IDE, telling the compiler how it should compile is the programmer's job. I'm not sure why you brought compiler options up, it has nothing to do with finding identifiers. – Lundin Jan 04 '22 at 15:15
  • @Lundin I care. Because giving a source file and a command to compile it seems simpler to me than describing how to do that in an IDE. Considering that I haven't used those in ages. Also, how many people have `gcc` installed but not your IDE? | Why the "Having `a.out`, how do I..."? The way it started, I wanted to take a look at how `Xorg` parses the arguments. I cloned the repository and... "How do I find `main()`?" I know neither `meson`, nor the codebase. A natural thing is to build it with debug information, and find the location of `main()` from that... – x-yuri Jan 04 '22 at 16:41
  • ...Considering that there are around 17 `main()`'s there. I tried to `grep` but the names of the files and dirs there make no sense to me. At some point I guessed that `meson.build` for `Xorg` is probably at `hw/xfree86/meson.build` (not sure), but it [says](https://gitlab.freedesktop.org/xorg/xserver/-/blob/38291fa86c00e1d3ee37612c9215b0c15da89051/hw/xfree86/meson.build#L42-45) that the entrypoint is at `mi/miinitext.c`, which has no `main()`. Now, humor me, try finding `main()` in `xserver` with your IDE and tell me how it did that. – x-yuri Jan 04 '22 at 16:41

2 Answers2

3

You can use gdb. Probably there's a better command, anyway I know of:

$ gdb -batch -ex "info function main" a.out
All functions matching regular expression "main":

File a.c:
8:   int main(void);
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • That's probably the best option. At least it's better than what I had in mind, because you don't need to bother with DWARF. One should note here though, that it [doesn't always](https://gist.github.com/x-yuri/cd45cc217ecdef6da06322058e37cc03) gives a correct result (no surprise here, since it doesn't work with `-flto`). With `gcc` you need at least [`-g`](https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#index-g), and don't need [`-flto`](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-flto). With Arch Build System you need `options=(debug !strip)`... – x-yuri Jan 03 '22 at 09:27
  • ...With `meson` you need to make sure there's no [`-D b_lto=true`](https://github.com/mesonbuild/meson/blob/0.60.3/mesonbuild/compilers/compilers.py#L333-L336). I tried to use [`readelf`](https://gist.github.com/x-yuri/a950e70d8af24d0a85782ddaa4464db8) though, and I've probably found, say, an empirical way to determine a location (which might not always work in theory). But I wonder how exactly `gdb` does it. There are multiple file name tables there. Any ideas? – x-yuri Jan 03 '22 at 09:27
  • That's kind of self-explanatory - sure, it doesn't work when there's no debug info in the executable or when the function got removed or inlined. C is a compiled programming language - you can't restore source code from executable, it's one way connection. `how exactly gdb does it` There's a debug section in the executable with list of symbols and locations, gdb reads that section. `file name tables there. Any ideas?` What are "file name tables"? Idea for ... ? – KamilCuk Jan 03 '22 at 09:45
  • Okay, there were a lot of links. I [tried](https://gist.github.com/x-yuri/a950e70d8af24d0a85782ddaa4464db8) what you suggest. My understanding is that `gdb` finds a symbol in `.debug_info`, then uses `DW_AT_decl_file` to find the filename in `.debug_line`. What I don't understand, there are multiple file name tables in `.debug_line`, how does it chooses the table? – x-yuri Jan 03 '22 at 10:03
0

In the executable is not normally the place to look for a function definition. You can do the compiling of all source files and run nm(1) on them to see if any of them has a definition of main. The executable is not the proper place as it will have no reference to the module it came from. The source files will be hard to follow (as some can have compilation directives with optionally compiled code /with a main definition in case you don't provide one/ that will make uncertaing the place where you find it) but the compiled module will have a reference to main to indicate the linker it can get it and solve all the main references from this file. The linker divides a compiled input into a set of segments and piles them up to the appropiate places in the processor memory map, and so, you get a messed final executable with pieces of each module mixed up, making it more difficult to check if a main definition is there (it applies to main or to any other function)

Output should be something like:

0000000000000c10 T main

in the file that conttains it. In the opposite, all files that require main and need it provided by the linker appear as:

                 U memset

instead.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31